BIND 10 master, updated. 1302962baac0113c3a9ff10f1317271ca060a1af [master] missing call to initLogger(). this can cause unexpected failure such as an infinite loop (or perhaps segfault) in tests. since this is critical and should be quite trivial, I'm pushing it right now.
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Jun 10 02:14:39 UTC 2011
The branch, master has been updated
via 1302962baac0113c3a9ff10f1317271ca060a1af (commit)
via ac65c96b59824fabc738a300ad2a36332e4ff01d (commit)
via 3255c92714737bb461fb67012376788530f16e40 (commit)
via 01e08ad7fd14a1683b8eb21f4aeb5e8ca8e54a03 (commit)
via a8414d057fa2ea8406a8911492bf91fb4e6d8166 (commit)
via 5e0454234b2e1975d7ecd79f8e40a43e6782f968 (commit)
via 9fa2a95177265905408c51d13c96e752b14a0824 (commit)
via 679661cb33158ce088dc3b7a3b5cf2fc9a7dad29 (commit)
via 31a30f5485859fd3df2839fc309d836e3206546e (commit)
via ec6aba3f5e5ea3f3e9e5b9d70b162fe6e0f33df2 (commit)
via e2e2a6756e459bb49446c22b89efa07306c35f21 (commit)
via 9611300c4a297500c0130d6b372be3bd409c6aaa (commit)
via a739faa43b38c39ae49950f3abc79d66ac0ab545 (commit)
via 078162ae556edceb66899017b9a3e613f2886475 (commit)
via c589e8b9c1f1d3b4fd4959bae8bfae5be04ae8fa (commit)
via 2e386bd4b400b73103900bcccb8b5166258448a6 (commit)
via dd46abf659db56f51b981ff675ef02ea92d70ed5 (commit)
via cbe4f685dbfcef07a17c6cef56d35abf12fd677b (commit)
via a4b89470c575878fbd0d3e1bddd45ed2a3289e6b (commit)
via 0c8063199fe37278da7fe03adb5723deb4263f82 (commit)
via e08c212ea82bf00c90eec566b1058862b82b78bf (commit)
via 63f62558f6490de53d57504ac48076165f18b9e8 (commit)
via 631651ce7025329af4264f8c576ef77ec3339288 (commit)
via 754c4ab6e60afe6f90ce7afda9670efe3debe13d (commit)
via 461fc3cb6ebabc9f3fa5213749956467a14ebfd4 (commit)
via 52cc6e6c2a7d61f93e17525b161b7c33ea4ade98 (commit)
via 25cb92eaa2253745fd27c957e8d576cd90b7b244 (commit)
via 73487153601d7ad71f04e6762cabd3b3712c860a (commit)
via ec4d7f29e1fade106ce33ebbbe0147330584a784 (commit)
via bf384859fca46c07f2e43c376ccaf9111bd3b1b9 (commit)
via 83e33ec80c0c6485d8b116b13045b3488071770f (commit)
via 542a15d604018ea73a5a28f26b30b92fb668e399 (commit)
via e4543dee376bae0e459f9008a2061f2a2529dcee (commit)
via acf47441057d4ce66bcb84b67038e1e99e822c72 (commit)
via 795329663bef70bf69fddd14267daff57abfadd4 (commit)
via 8a83c13c2037e69e64474131916b6c53ececef34 (commit)
via cd4ffa3493b2e1f3c53e83b60dd4f7d03dbb518a (commit)
via a9d0e238a2ddfc35c772e2a72a3c73ced70538c4 (commit)
via a86e015d7c5fd0b0d8286523aa5d7e3b036f8589 (commit)
via 92113f50b12c221ca1db5dbccd51d762be5d4f6e (commit)
via 16717b1380deba97032b3ec698c051cbcb032263 (commit)
via 364d07e849b394da94b715450814ebf559ca9cb5 (commit)
via 5638c66218f33efb7fbf7e3700d49ff7b6b0f090 (commit)
via 1ef6e04be1902dfad65d7df155756ab5a73aa843 (commit)
via 7855203ec77dd6165607297f90d0be20734fe692 (commit)
via e006dc3e92ac9a8bc8792bdf9c26fb18f064f4f8 (commit)
via bddb6473013e4cc91d4a76f182702d0bd1856f2c (commit)
via 2e5251ee15359eebf7d081c265948713222ef850 (commit)
via 3b2040e2af2f8139c1c319a2cbc429035d93f217 (commit)
via c9ec49aaf765eef5ba3450ad0b60ae031fc571c6 (commit)
via 77ac9b0e168fd353a8d4826e4aa46fcf98b377cf (commit)
via 7f8b0c9ad5ad478b0ed0df4a8a9138449b6f63b4 (commit)
via 2d1592a9e5aa5d212336af266e07ed20e8b56b06 (commit)
via 0a6fabd5e27753d3cb3827f05c3d279ba31cb1c2 (commit)
via 7385c5e065fe7ab4dfec519d97eea2788306b00c (commit)
via 0ac3a688424c0f5da2e0bd7086dfacf846c47970 (commit)
via 78fa7f965cb3bdb319a88028960e69fbf8773977 (commit)
via e245b3b40b9508a4579e0c9506df0cb3f8bc208a (commit)
via e4d3b7c6dc04c8dc443610bc45bafef86519c92c (commit)
via 182328dfa329c085df1f334a32d68ad925dab476 (commit)
via 5f20e1ed0833a7dc5816cee885235c42652cb517 (commit)
via 21b0e9d23d1c72940fb01f3dc9ad6c7975abaf5d (commit)
via 824f8da33c64169ea6e768f899ba7501a3536823 (commit)
via ff69cf49ecc375485209da7caabea778b5ff1ca2 (commit)
via 38b3546867425bd64dbc5920111a843a3330646b (commit)
via ec4ebeb8ecfae6b0e3cc6fedabb7e2f84509c930 (commit)
via 48e10c2530fe52c9bde6197db07674a851aa0f5d (commit)
via c4a9bf2722c9650c1c0f4290670ff33c0b7af87c (commit)
via 17494c7a2f58e91cdcaa8b6e28bb275d49f25437 (commit)
via 7c4d8ab93f853f7abee812976340008554f25fb6 (commit)
via 684a395b40dd02ef04d3a914b7f90ea96e808f5c (commit)
via 4e058c0c696584bac4b299f241ca5e74d7121b06 (commit)
via d22ff00401ed90df31342dcce12bb4ceb493f232 (commit)
via a61bc91e4109168327481710f17affe728af80d5 (commit)
via c58ee77ef68b2cf49db6af85ede886580ad2b6bc (commit)
via 601d7f0170b1f9f929acadd3c37d60c5876ca7be (commit)
via ef8b3f326fe9ab9aab0050071c6f8975b8ecd354 (commit)
via 51f6bda14d754aa6aea474300c8c51f15f32f0be (commit)
via 711a621387d48993712031be9164510de7b27054 (commit)
via 4948469dafe0a5ff130706ff2fb13676c417a538 (commit)
via 2626464bbbb35e29f01a7e5a532d06da8feef837 (commit)
via 83a82bdac7711760eed682b91f6be4435606a0dd (commit)
via 616019706c0101316835f85843c59439e20a1c8e (commit)
via cb6be8450ae82e705dc4c65ca4415b1ed77abd6b (commit)
via a705f09ca7cf67fdf8947716d27e49f1f9c58366 (commit)
via 9f8b63669172d090e653b68b18b162539428663a (commit)
via ddcbd508353cff40985a2e40334a82d91bf95341 (commit)
via 560e7210fa3006c98c711e96cdbac6f9a0b391a0 (commit)
via 9500689fabae67565bee456d33953ae7b139a209 (commit)
via 505839e902948071f5e7876d274db4345b28b49d (commit)
via 78def66fe5de6f50a555b24d3134d5ec0dc32021 (commit)
via db96ce78efca85a1953fcb14d4ba24b7989044cc (commit)
via e2f1f9c450d7cd9e46bd23cef80be7db829d44c5 (commit)
via 56e8727ef471d54b56a88f340d19e2ee05d898bf (commit)
via 137e70ef17439961ac3ce8fa288675502cd26178 (commit)
via a9a606dd7a35d81932f970e00cdf38b466eee2d4 (commit)
via e848d7f24f16319fa07ded7a76af27187a8348ee (commit)
via db21e8ab55f132828542f1a34642d98bbaf6d8ee (commit)
via 31d3f525dc01638aecae460cb4bc2040c9e4df10 (commit)
via 489ef1ad9701548d0987c45cab34d33d8dcb73fa (commit)
via 95f7b6ea18efc84f160053bec169ab8a736cbb1a (commit)
via c24dd6f877460227e6eed02d0cca0956366748cb (commit)
via 2f9c3c1f2f692d16e55022f90333abbf7e3f13cf (commit)
via 4d4d0ee24097af2ec942ca78025c95908063a4b3 (commit)
via 14153d9340c8220b4cc4f0088a14f6260b3af548 (commit)
via e402f8d476894b34d7bce97fbc961123e4775b4f (commit)
via 9b93c0778662b587f0a526583fb011ebdf9788d2 (commit)
via e219a9fb24d6efe15066526ded67093f1a59c8ad (commit)
via 99a107f4ab33c0791df351408c76c07f5820cde5 (commit)
via f843346fdfc748cb5b67d079958202c8d4e32e0c (commit)
via 54c201f10077610d7e09a7787d89d17264761f5d (commit)
via 5637b1961aafcdc6950d22aa7a3637221a57e99a (commit)
via 39238e406f2e0cdd63a71f4b4b8700988e1dac9e (commit)
via 1a32f61cafb9f735815bf8f4abb0d8e1019269c3 (commit)
via f3395938a1ead66ef30ab1d261e3a38fea16faa4 (commit)
via 36e95aa1164d24b5b2c46d64958c709a9a02fd51 (commit)
via 0692e63e77ba5f6e1dd24619f215c06a53a48845 (commit)
via c9d69c549110808b6314bac44b357b4f0fb2b699 (commit)
via fd99c6922384541b484e5e3ac4422d8472011b42 (commit)
via 432a1a86cea043993348f31d14628548e267e3b8 (commit)
via fe5e7003544e4e8f18efa7b466a65f336d8c8e4d (commit)
via 475624c79f83adc6befd4d631eeaf83d83fe89f9 (commit)
via 3d05a83e9466e6075dec6e4b7a1502989d9205e9 (commit)
via acfbd3b209c50aeff46fc09f9d792a457f51722d (commit)
via 9e48d735ae41dd79b6ff856ea4e5ba79112f440c (commit)
via cfa31a99243065c40dc3f5a6393bb8ec923d2bd2 (commit)
via 5178a891fb3713609ade921d010bed453498f355 (commit)
via 67727b623a4889ee0e38bd0ad9aaba710e722e4f (commit)
via b31501e6a7d8cb5c1b3714f289d9c55753447d83 (commit)
via e9975a7383a299789fd18b184880a00dfb0e15ed (commit)
via c8cf64492fbb3329d10b15ff660c9f262e46ae1f (commit)
via 1ccec996459f9cea1bc32b6d736692fd087aae0e (commit)
via 4f06531cf0b40476bbfd7d6a4312bacb1d958ffe (commit)
via c1d2bae691446b1fb44d3281092a7529f2f4f648 (commit)
via 5e6de42e41584afd09a579b08d25902c9db88927 (commit)
via b39ac098a37803588a3a801d91b85170ddaa7137 (commit)
via a5dd2311e2083f2c32fc55b5339b1e064e8b7de3 (commit)
via e57fcdbaee0fe7562168a363c1f3273a92fa595a (commit)
via dc44fdda4e0705993f44275d22b2daa45d4bae6f (commit)
via 160519b6e3c5da340011b454e034a188529a4cc8 (commit)
via 5538d85d751405c573cfc0491b1e663bb7ce19f5 (commit)
via e3a81c18ff3f55727c1308bb894a8eb03e8f48b1 (commit)
via d6b120403a24c9c233fa70ff8fca4a5da39f209e (commit)
via e156ec53472f22bbb890ebc76a1acdd9a9eda70c (commit)
via f80bba65ac947cdc2f99253623e976c815b63140 (commit)
via d694e5ded809ef2842eb5101e4d03e6e657e4671 (commit)
via 5d33e8c43d18b6aef9461086c8fbd5ee493b55df (commit)
via fae2d0d5854141a9af7c1177c663a0910ab18ad0 (commit)
via 14a1dcf8c6d299b7b7d91b27c4bd73b06e1fd61d (commit)
via ce11fa4222196986805f54ab5b67b77dd5fe8e47 (commit)
via 74402ae4cf3274fb20bc3183941c4099dc672b89 (commit)
via c4412dfaa0bb8c6e95cb4476e80b648cfa9288f2 (commit)
via b2c418d1d6aed9e2eb599941218e1f8c0d13a445 (commit)
via 48fab55d59089444ac9dbd81ea3c6978f55f9474 (commit)
via efb25130e7c98335f72741cb62c9b3cf4ab076b3 (commit)
via e37672ef8e814456d53772220d885c91941db7bd (commit)
via a3edee3da65d09f2b00256277ed8d8a608c9c627 (commit)
via b8855cafa94a12af7e087a4866367f2336e10bdb (commit)
via 682108d6e371706f340cfe8575d8e00d1dab09cc (commit)
via 6ea285d4f7cf6a9fd18bd9a3811944fc02d0e34c (commit)
via 1aa773d84cd6431aa1483eb34a7f4204949a610f (commit)
via 5519b6cb34e655c96dca4ec8be2a3eabda941f3c (commit)
via ec6446047b5007290ca4d6d31e67239c424f33bb (commit)
via de552586600ecd4123b904e65d09c3ef0717fc2a (commit)
via 58ec390c5c70c0c7625e3f52caf4c4f20b3bffa3 (commit)
via 34ebe17e3d731f19c6c44a8777b994241b5de7ee (commit)
via 67c6c4489b2daeb6001cf2f462cc189f2c841b5a (commit)
via 41139644452bb11a162254b442ab611644ceb603 (commit)
via c84b3ecae4d2d0b532c64c958857f8104dbae923 (commit)
via 27ba6b2117e3ab88aedaa904292707871e131393 (commit)
via 76ae47d1f6061a09f4a8e20852a9874ea28a4e19 (commit)
via bfaafb81eac3c4ea98cb63e2c0bdb8fc02105b6c (commit)
via 6a9f0125c633e6203e5fda37e6220ea862038df6 (commit)
via 990a0cff9891fce08b3e163720dfa08fffdede5f (commit)
via 54f86d7796c55289518befaefdcdd7d84ebefa88 (commit)
via e66cc9c2e0eca69c62a3a61133fc4ba220274bc7 (commit)
via 24fd003fcc458c673803b953ca857a96bfa5183c (commit)
via a9d323bf4df6aeb6333b057514b748d05febc1a9 (commit)
via e1df41b8ba21d04560ad4b65837bf7bb32fbdf34 (commit)
via 2af8dc8ec9301a935228568f80f8ba18531a3ffc (commit)
via 862e13c5c852b8ea55c1b53a67803105d473a342 (commit)
via ee0e702fa85ebecb95ac89aeba5959b111ba5d57 (commit)
via 0dcde0e52b99cade8f584751b2d2756c20e78625 (commit)
via f1213a3d4df71a4009a3f9a09a9aab002b42ce35 (commit)
via f39c8aea4a0f9dc4b910c003b336e124149ab88b (commit)
via 5dcd9589aff3bc2a91ad2dbef98cd9012ed9b637 (commit)
via 857abf9ee880aecc9039b993e73683f1a6019cf6 (commit)
via 5db2d64ddcd9f4eec0db6bbb6a160c95ac81cc6e (commit)
via 37ded0b31a0a0dcadb93da7bd85248b90d9af57f (commit)
via 50dd99f81efde9267569def411a470e082ac312b (commit)
via 2273e3e71f5879cdf51f956c19a153f4280d9531 (commit)
via c926e1e8bf4eedc6c999c6ad1a1f0fff96783154 (commit)
via 192aefd97c825f7636e0885eecef9a5834e53d05 (commit)
via 67d9442a23eb4fab4dff3f4d1ab142948e21e880 (commit)
via 5f74b5f0820d7bf4aa50d0341c9316e6a915738e (commit)
via 030c77386b5018d30282cda2fb6aabe620a0f15b (commit)
via a206dbd5432240440b6bbc63ff3165ad579d5efb (commit)
via 6342eda010ae512cb972e70f3824ca0de638c293 (commit)
via ff008c094a577e61f619cc39df2eab858dee0fe5 (commit)
via 4fcd0d3125dd7d8441f33c3ebf8b63e5b2093a68 (commit)
via ede93598806f8ddb21d193eb002fad8218416e40 (commit)
via 35ac625edfbc78dde6ccf78f8d577f3d8edadbce (commit)
via a8307030f7af9fc88e3e66b6eefcc89f6b6e15c5 (commit)
via 07a778c5592002042269785ec41bf4d93fc7db91 (commit)
via 9cc0c06cac86d3460ee1f5b5e2c8669d9709663e (commit)
via b8da54961c78a690c6bf02618d4c28fb9d320177 (commit)
via d098ac2ee97872ebcd8366cb700d7d2a5e668b8c (commit)
via af788f1fef2cbdb63b05f14106f907dff87fd6bf (commit)
via a94c70e333e3082a55f8a82ff026aae9b35964ad (commit)
via b5673b0b0860921b516d9a0e514891e78e444578 (commit)
via 4cea30b55073337c32d03bbd9dc813eb92795c2e (commit)
via f0c1ac2d7613802bbbc9545684ba0a9346bfdc0f (commit)
via 5d422dbf6640bae02dc13ad7c9406fe13f42a1ef (commit)
via 072ba32545ec255923699c0c181345910f86a208 (commit)
via 1f91575ae48846e3a97a0c3921dd808470ba4427 (commit)
via 523ab3a9278385abc26e1438803499a66e260f43 (commit)
via 8e4fcdf22e1b5d14f740276e3a6c34a90bf3117c (commit)
via b02c482b25d489b723927f45c4cfe3fedd89f5b7 (commit)
via 99240dbca4848d3aa76b34f5ff0d6da4d6bf911c (commit)
via 3f27e2dc3c0ce961a95e4791604e3cb12fd43dfa (commit)
via 50ca3176a95f7ca760c0749d7a92634e2526369d (commit)
via 08878f6bfb271301564ad307339d2599bc6d951c (commit)
via fd9855894957b318876a9cb9a0dbe2b4cbbdd4b6 (commit)
via 0f07b7c15231aa778e693e0f2b36d32e1023c431 (commit)
via a28db69a6e8e3548e8e41f62999fff18dbd33bec (commit)
via e3ab39868457157166a8b7b2f1753555409426b9 (commit)
via cba6db1c8d894a7f30ae820a49aea3d0ff8c18be (commit)
via d4a2c864bf1952f1bcccead59159462519c58e10 (commit)
via ab5868a8dc4c6859f772219daddb7775848d3dc6 (commit)
via 9c4b079aca67cffe9385e54671f8eb9ed232e1e5 (commit)
via b1b486e59d86633e7b5e17aeedf0f78688ecef05 (commit)
via 45fb91884dc0741384a14e7f52633b0566779dff (commit)
via c166c0c96a476eea511fe363b90df3d78fae7506 (commit)
via 0e1eaada6f38f23e7de9404a94b2cd5dd2794879 (commit)
via 16f90854f8957d12e2861bc77303f186402a8805 (commit)
via efea92bd3f50b23ed5d551cd7f140abe47959bfd (commit)
from c37ebedf94c5dbbed3c685272a0cdc4bea67fb04 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 61 +++-
Makefile.am | 3 +
configure.ac | 63 +++-
src/bin/auth/Makefile.am | 2 +-
src/bin/auth/auth_config.cc | 14 +
src/bin/auth/auth_srv.cc | 100 ++++-
src/bin/auth/auth_srv.h | 11 +
src/bin/auth/benchmarks/Makefile.am | 1 +
src/bin/auth/main.cc | 16 +-
src/bin/auth/tests/Makefile.am | 1 +
src/bin/auth/tests/auth_srv_unittest.cc | 138 +++++++
src/bin/auth/tests/config_unittest.cc | 7 +
src/bin/auth/tests/run_unittests.cc | 5 +-
src/bin/cfgmgr/plugins/Makefile.am | 1 +
src/bin/cfgmgr/plugins/b10logging.py | 96 +++++
src/bin/cfgmgr/plugins/logging.spec | 81 ++++
src/bin/cfgmgr/plugins/tests/tsig_keys_test.py | 2 +-
src/bin/cmdctl/Makefile.am | 5 +-
src/bin/resolver/Makefile.am | 19 +-
src/bin/resolver/main.cc | 46 ++-
src/bin/resolver/resolver.cc | 131 ++++--
src/bin/resolver/resolver_log.cc | 19 +
src/bin/resolver/resolver_log.h | 49 +++
src/bin/resolver/resolverdef.mes | 193 +++++++++
src/bin/resolver/tests/Makefile.am | 14 +-
src/bin/resolver/tests/run_unittests.cc | 5 +-
src/bin/sockcreator/tests/Makefile.am | 5 +-
src/bin/sockcreator/tests/run_unittests.cc | 3 +-
src/bin/xfrout/tests/xfrout_test.py.in | 216 ++++++++++-
src/bin/xfrout/xfrout.py.in | 110 ++++-
src/bin/xfrout/xfrout.spec.pre.in | 12 +
src/lib/asiodns/io_fetch.cc | 13 +-
src/lib/asiodns/tests/Makefile.am | 4 +-
src/lib/asiodns/tests/io_fetch_unittest.cc | 3 +
src/lib/asiodns/tests/run_unittests.cc | 7 +-
src/lib/asiolink/tests/Makefile.am | 5 +-
src/lib/asiolink/tests/run_unittests.cc | 10 +-
src/lib/bench/tests/Makefile.am | 6 +-
src/lib/bench/tests/run_unittests.cc | 3 +-
src/lib/cache/tests/Makefile.am | 28 +-
src/lib/cache/tests/run_unittests.cc | 3 +-
src/lib/cc/tests/Makefile.am | 1 +
src/lib/cc/tests/run_unittests.cc | 3 +-
src/lib/config/ccsession.cc | 213 +++++++++--
src/lib/config/ccsession.h | 40 ++-
src/lib/config/config_data.cc | 136 +++++--
src/lib/config/config_data.h | 10 +
src/lib/config/configdef.mes | 7 +
src/lib/config/tests/Makefile.am | 5 +-
src/lib/config/tests/ccsession_unittests.cc | 92 ++++-
src/lib/config/tests/config_data_unittests.cc | 20 +-
.../config/tests/data_def_unittests_config.h.in | 1 +
src/lib/config/tests/fake_session.cc | 22 +-
src/lib/config/tests/fake_session.h | 9 +
src/lib/config/tests/run_unittests.cc | 10 +-
src/lib/config/tests/testdata/Makefile.am | 2 +
src/lib/config/tests/testdata/spec30.spec | 45 ++
src/lib/config/tests/testdata/spec31.spec | 63 +++
src/lib/cryptolink/crypto_hmac.cc | 8 +-
src/lib/cryptolink/tests/Makefile.am | 5 +-
src/lib/cryptolink/tests/crypto_unittests.cc | 428 +++++++-------------
src/lib/cryptolink/tests/run_unittests.cc | 3 +-
src/lib/datasrc/tests/Makefile.am | 7 +-
src/lib/datasrc/tests/run_unittests.cc | 6 +-
src/lib/dns/tests/Makefile.am | 7 +-
src/lib/dns/tests/run_unittests.cc | 3 +-
src/lib/dns/tests/tsig_unittest.cc | 23 +
src/lib/exceptions/tests/run_unittests.cc | 3 +
src/lib/log/Makefile.am | 25 +-
src/lib/log/README | 99 ++----
src/lib/log/compiler/Makefile.am | 8 +-
src/lib/log/compiler/message.cc | 266 +++++++-----
src/lib/log/debug_levels.h | 29 --
src/lib/log/impldef.cc | 29 ++
src/lib/log/impldef.h | 18 +
src/lib/log/impldef.mes | 38 ++
src/lib/log/log_formatter.h | 68 +++-
src/lib/log/logger.cc | 41 +-
src/lib/log/logger.h | 139 ++++----
src/lib/log/logger_impl.cc | 200 +++-------
src/lib/log/logger_impl.h | 144 +++----
src/lib/log/logger_impl_log4cxx.cc | 242 -----------
src/lib/log/logger_impl_log4cxx.h | 315 --------------
src/lib/log/logger_level.cc | 48 +++
src/lib/log/logger_level.h | 76 ++++
src/lib/log/logger_level_impl.cc | 217 ++++++++++
src/lib/log/logger_level_impl.h | 127 ++++++
src/lib/log/logger_levels.h | 42 --
src/lib/log/logger_manager.cc | 183 +++++++++
src/lib/log/logger_manager.h | 141 +++++++
src/lib/log/logger_manager_impl.cc | 228 +++++++++++
src/lib/log/logger_manager_impl.h | 171 ++++++++
src/lib/log/logger_name.cc | 59 +++
src/lib/log/logger_name.h | 57 +++
src/lib/log/logger_specification.h | 156 +++++++
src/lib/log/logger_support.cc | 112 +-----
src/lib/log/logger_support.h | 23 +-
src/lib/log/messagedef.cc | 8 +-
src/lib/log/messagedef.h | 5 +-
src/lib/log/messagedef.mes | 12 +
src/lib/log/output_option.cc | 54 +++
src/lib/log/output_option.h | 85 ++++
src/lib/log/root_logger_name.cc | 44 --
src/lib/log/root_logger_name.h | 46 ---
src/lib/log/tests/Makefile.am | 49 ++-
src/lib/log/tests/console_test.sh.in | 69 ++++
src/lib/log/tests/destination_test.sh.in | 94 +++++
src/lib/log/tests/local_file_test.sh.in | 86 ++++
src/lib/log/tests/log_formatter_unittest.cc | 37 +-
src/lib/log/tests/logger_example.cc | 305 ++++++++++++++
src/lib/log/tests/logger_impl_log4cxx_unittest.cc | 91 -----
src/lib/log/tests/logger_level_impl_unittest.cc | 171 ++++++++
src/lib/log/tests/logger_level_unittest.cc | 82 ++++
src/lib/log/tests/logger_manager_unittest.cc | 321 +++++++++++++++
src/lib/log/tests/logger_name_unittest.cc | 77 ++++
src/lib/log/tests/logger_specification_unittest.cc | 96 +++++
src/lib/log/tests/logger_support_test.cc | 106 -----
src/lib/log/tests/logger_unittest.cc | 111 +++---
src/lib/log/tests/output_option_unittest.cc | 66 +++
src/lib/log/tests/root_logger_name_unittest.cc | 50 ---
src/lib/log/tests/run_time_init_test.sh.in | 90 ----
src/lib/log/tests/run_unittests.cc | 6 +-
src/lib/log/tests/severity_test.sh.in | 91 +++++
src/lib/log/tests/tempdir.h.in | 29 ++
src/lib/log/tests/xdebuglevel_unittest.cc | 203 ---------
src/lib/log/xdebuglevel.cc | 146 -------
src/lib/log/xdebuglevel.h | 162 --------
src/lib/nsas/tests/Makefile.am | 5 +-
src/lib/nsas/tests/run_unittests.cc | 15 +-
src/lib/python/bind10_config.py.in | 3 +-
src/lib/python/isc/config/ccsession.py | 16 +-
src/lib/python/isc/config/cfgmgr.py | 9 +-
src/lib/python/isc/config/config_data.py | 87 +++--
src/lib/python/isc/config/module_spec.py | 4 +-
src/lib/python/isc/config/tests/ccsession_test.py | 25 ++
src/lib/python/isc/config/tests/cfgmgr_test.py | 7 +-
.../python/isc/config/tests/config_data_test.py | 29 ++
src/lib/python/isc/datasrc/sqlite3_ds.py | 38 +-
src/lib/python/isc/datasrc/tests/Makefile.am | 2 +
.../python/isc/datasrc/tests/sqlite3_ds_test.py | 58 +++-
src/lib/python/isc/notify/notify_out.py | 28 +-
src/lib/python/isc/notify/tests/notify_out_test.py | 48 ++-
src/lib/resolve/Makefile.am | 22 +-
src/lib/resolve/recursive_query.cc | 172 ++++++---
src/lib/resolve/resolve_log.cc | 26 ++
src/lib/resolve/resolve_log.h | 53 +++
src/lib/resolve/resolvedef.mes | 155 +++++++
src/lib/resolve/tests/Makefile.am | 9 +-
src/lib/resolve/tests/run_unittests.cc | 5 +-
src/lib/server_common/keyring.cc | 10 +-
src/lib/server_common/keyring.h | 16 +-
src/lib/server_common/tests/Makefile.am | 4 +-
src/lib/server_common/tests/keyring_test.cc | 25 +-
src/lib/server_common/tests/run_unittests.cc | 3 +-
src/lib/testutils/srv_test.cc | 8 +-
src/lib/testutils/srv_test.h | 3 +-
src/lib/util/Makefile.am | 4 +-
src/lib/util/encode/base_n.cc | 35 ++-
src/lib/util/io/Makefile.am | 2 -
src/lib/util/io/tests/Makefile.am | 25 --
src/lib/util/io/tests/fd_share_tests.cc | 74 ----
src/lib/util/io/tests/fd_tests.cc | 66 ---
src/lib/util/io/tests/run_unittests.cc | 22 -
src/lib/util/tests/Makefile.am | 24 +-
src/lib/util/tests/base32hex_unittest.cc | 7 +-
src/lib/util/tests/base64_unittest.cc | 8 +-
src/lib/util/tests/fd_share_tests.cc | 74 ++++
src/lib/util/tests/fd_tests.cc | 66 +++
src/lib/util/tests/run_unittests.cc | 4 +-
src/lib/util/unittests/Makefile.am | 12 +
src/lib/util/unittests/run_all.cc | 95 +++++
src/lib/util/unittests/run_all.h | 52 +++
tests/tools/badpacket/Makefile.am | 2 +-
tests/tools/badpacket/tests/Makefile.am | 8 +-
tests/tools/badpacket/tests/run_unittests.cc | 3 +-
175 files changed, 6977 insertions(+), 3267 deletions(-)
create mode 100644 src/bin/cfgmgr/plugins/b10logging.py
create mode 100644 src/bin/cfgmgr/plugins/logging.spec
create mode 100644 src/bin/resolver/resolver_log.cc
create mode 100644 src/bin/resolver/resolver_log.h
create mode 100644 src/bin/resolver/resolverdef.mes
create mode 100644 src/lib/config/tests/testdata/spec30.spec
create mode 100644 src/lib/config/tests/testdata/spec31.spec
delete mode 100644 src/lib/log/debug_levels.h
create mode 100644 src/lib/log/impldef.cc
create mode 100644 src/lib/log/impldef.h
create mode 100644 src/lib/log/impldef.mes
delete mode 100644 src/lib/log/logger_impl_log4cxx.cc
delete mode 100644 src/lib/log/logger_impl_log4cxx.h
create mode 100644 src/lib/log/logger_level.cc
create mode 100644 src/lib/log/logger_level.h
create mode 100644 src/lib/log/logger_level_impl.cc
create mode 100644 src/lib/log/logger_level_impl.h
delete mode 100644 src/lib/log/logger_levels.h
create mode 100644 src/lib/log/logger_manager.cc
create mode 100644 src/lib/log/logger_manager.h
create mode 100644 src/lib/log/logger_manager_impl.cc
create mode 100644 src/lib/log/logger_manager_impl.h
create mode 100644 src/lib/log/logger_name.cc
create mode 100644 src/lib/log/logger_name.h
create mode 100644 src/lib/log/logger_specification.h
create mode 100644 src/lib/log/output_option.cc
create mode 100644 src/lib/log/output_option.h
delete mode 100644 src/lib/log/root_logger_name.cc
delete mode 100644 src/lib/log/root_logger_name.h
create mode 100755 src/lib/log/tests/console_test.sh.in
create mode 100755 src/lib/log/tests/destination_test.sh.in
create mode 100755 src/lib/log/tests/local_file_test.sh.in
create mode 100644 src/lib/log/tests/logger_example.cc
delete mode 100644 src/lib/log/tests/logger_impl_log4cxx_unittest.cc
create mode 100644 src/lib/log/tests/logger_level_impl_unittest.cc
create mode 100644 src/lib/log/tests/logger_level_unittest.cc
create mode 100644 src/lib/log/tests/logger_manager_unittest.cc
create mode 100644 src/lib/log/tests/logger_name_unittest.cc
create mode 100644 src/lib/log/tests/logger_specification_unittest.cc
delete mode 100644 src/lib/log/tests/logger_support_test.cc
create mode 100644 src/lib/log/tests/output_option_unittest.cc
delete mode 100644 src/lib/log/tests/root_logger_name_unittest.cc
delete mode 100755 src/lib/log/tests/run_time_init_test.sh.in
create mode 100755 src/lib/log/tests/severity_test.sh.in
create mode 100644 src/lib/log/tests/tempdir.h.in
delete mode 100644 src/lib/log/tests/xdebuglevel_unittest.cc
delete mode 100644 src/lib/log/xdebuglevel.cc
delete mode 100644 src/lib/log/xdebuglevel.h
create mode 100644 src/lib/resolve/resolve_log.cc
create mode 100644 src/lib/resolve/resolve_log.h
create mode 100644 src/lib/resolve/resolvedef.mes
delete mode 100644 src/lib/util/io/tests/Makefile.am
delete mode 100644 src/lib/util/io/tests/fd_share_tests.cc
delete mode 100644 src/lib/util/io/tests/fd_tests.cc
delete mode 100644 src/lib/util/io/tests/run_unittests.cc
create mode 100644 src/lib/util/tests/fd_share_tests.cc
create mode 100644 src/lib/util/tests/fd_tests.cc
create mode 100644 src/lib/util/unittests/run_all.cc
create mode 100644 src/lib/util/unittests/run_all.h
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index e44caa2..dd22211 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,65 @@
+254. [bug] jinmei
+ b10-xfrout: failed to send notifies over IPv6 correctly.
+ (Trac964, git 3255c92714737bb461fb67012376788530f16e40)
+
+253. [func] jelte
+ Add configuration options for logging through the virtual module
+ Logging.
+ (Trac 736, git 9fa2a95177265905408c51d13c96e752b14a0824)
+
+252. [func] stephen
+ Add syslog as destination for logging.
+ (Trac976, git 31a30f5485859fd3df2839fc309d836e3206546e)
+
+251. [bug]* jinmei
+ Make sure bindctl private files are non readable to anyone except
+ the owner or users in the same group. Note that if BIND 10 is run
+ with changing the user, this change means that the file owner or
+ group will have to be adjusted. Also note that this change is
+ only effective for a fresh install; if these files already exist,
+ their permissions must be adjusted by hand (if necessary).
+ (Trac870, git 461fc3cb6ebabc9f3fa5213749956467a14ebfd4)
+
+250. [bug] ocean
+ src/lib/util/encode, in some conditions, the DecodeNormalizer's
+ iterator may reach the end() and when later being dereferenced
+ it will cause crash on some platform.
+ (Trac838, git 83e33ec80c0c6485d8b116b13045b3488071770f)
+
+249. [func] jerry
+ xfrout: add support for TSIG verification.
+ (Trac816, git 3b2040e2af2f8139c1c319a2cbc429035d93f217)
+
+248. [func] stephen
+ Add file and stderr as destinations for logging.
+ (Trac555, git 38b3546867425bd64dbc5920111a843a3330646b)
+
+247. [func] jelte
+ Upstream queries from the resolver now set EDNS0 buffer size.
+ (Trac834, git 48e10c2530fe52c9bde6197db07674a851aa0f5d)
+
+246. [func] stephen
+ Implement logging using log4cplus (http://log4cplus.sourceforge.net)
+ (Trac899, git 31d3f525dc01638aecae460cb4bc2040c9e4df10)
+
+245. [func] vorner
+ Authoritative server can now sign the answers using TSIG
+ (configured in tsig_keys/keys, list of strings like
+ "name:<base64-secret>:sha1-hmac"). It doesn't use them for
+ ACL yet, only verifies them and signs if the request is signed.
+ (Trac875, git fe5e7003544e4e8f18efa7b466a65f336d8c8e4d)
+
+244. [func] stephen
+ In unit tests, allow the choice of whether unhandled exceptions are
+ caught in the unit test program (and details printed) or allowed to
+ propagate to the default exception handler. See the bind10-dev thread
+ https://lists.isc.org/pipermail/bind10-dev/2011-January/001867.html
+ for more details.
+ (Trac #542, git 1aa773d84cd6431aa1483eb34a7f4204949a610f)
+
243. [func]* feng
Add optional hmac algorithm SHA224/384/812.
- (Trac#782,git 77d792c9d7c1a3f95d3e6a8b721ac79002cd7db1).
+ (Trac#782, git 77d792c9d7c1a3f95d3e6a8b721ac79002cd7db1)
bind10-devel-20110519 released on May 19, 2011
diff --git a/Makefile.am b/Makefile.am
index bab6679..b07ef0f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -38,8 +38,11 @@ report-cpp-coverage:
c++/4.4\*/ext/\* \
c++/4.4\*/\*-\*/bits/\* \
boost/\* \
+ botan/\* \
ext/asio/\* \
+ ext/coroutine/\* \
gtest/\* \
+ log4cplus/\* \
usr/include/\* \
tests/\* \
unittests/\* \
diff --git a/configure.ac b/configure.ac
index 2324b21..c0d21fa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -447,6 +447,55 @@ AC_LINK_IFELSE(
CPPFLAGS=$CPPFLAGS_SAVED
LDFLAGS=$LDFLAGS_SAVED
+# Check for log4cplus
+log4cplus_path="yes"
+AC_ARG_WITH([log4cplus],
+ AC_HELP_STRING([--with-log4cplus=PATH],
+ [specify exact directory of log4cplus library and headers]),
+ [log4cplus_path="$withval"])
+if test "${log4cplus_path}" == "no" ; then
+ AC_MSG_ERROR([Need log4cplus])
+elif test "${log4cplus_path}" != "yes" ; then
+ LOG4CPLUS_INCLUDES="-I${log4cplus_path}/include"
+ LOG4CPLUS_LDFLAGS="-L${log4cplus_path}/lib"
+else
+# If not specified, try some common paths.
+ log4cplusdirs="/usr/local /usr/pkg /opt /opt/local"
+ for d in $log4cplusdirs
+ do
+ if test -f $d/include/log4cplus/logger.h; then
+ LOG4CPLUS_INCLUDES="-I$d/include"
+ LOG4CPLUS_LDFLAGS="-L$d/lib"
+ break
+ fi
+ done
+fi
+
+LOG4CPLUS_LDFLAGS="$LOG4CPLUS_LDFLAGS -llog4cplus $MULTITHREADING_FLAG"
+
+AC_SUBST(LOG4CPLUS_LDFLAGS)
+AC_SUBST(LOG4CPLUS_INCLUDES)
+
+CPPFLAGS_SAVED=$CPPFLAGS
+CPPFLAGS="$LOG4CPLUS_INCLUDES $CPPFLAGS"
+LDFLAGS_SAVED="$LDFLAGS"
+LDFLAGS="$LOG4CPLUS_LDFLAGS $LDFLAGS"
+
+AC_CHECK_HEADERS([log4cplus/logger.h],,AC_MSG_ERROR([Missing required header files.]))
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([#include <log4cplus/logger.h>
+ ],
+ [using namespace log4cplus;
+ Logger logger = Logger::getInstance("main");
+ ])],
+ [AC_MSG_RESULT([checking for log4cplus library... yes])],
+ [AC_MSG_RESULT([checking for log4cplus library... no])
+ AC_MSG_ERROR([Needs log4cplus library])]
+)
+
+CPPFLAGS=$CPPFLAGS_SAVED
+LDFLAGS=$LDFLAGS_SAVED
+
#
# Configure Boost header path
#
@@ -775,7 +824,6 @@ AC_CONFIG_FILES([Makefile
src/lib/server_common/tests/Makefile
src/lib/util/Makefile
src/lib/util/io/Makefile
- src/lib/util/io/tests/Makefile
src/lib/util/unittests/Makefile
src/lib/util/pyunittests/Makefile
src/lib/util/tests/Makefile
@@ -842,7 +890,11 @@ AC_OUTPUT([doc/version.ent
src/lib/dns/tests/testdata/gen-wiredata.py
src/lib/cc/session_config.h.pre
src/lib/cc/tests/session_unittests_config.h
- src/lib/log/tests/run_time_init_test.sh
+ src/lib/log/tests/console_test.sh
+ src/lib/log/tests/destination_test.sh
+ src/lib/log/tests/local_file_test.sh
+ src/lib/log/tests/severity_test.sh
+ src/lib/log/tests/tempdir.h
src/lib/util/python/mkpywrapper.py
src/lib/server_common/tests/data_path.h
tests/system/conf.sh
@@ -869,7 +921,10 @@ AC_OUTPUT([doc/version.ent
chmod +x src/bin/msgq/tests/msgq_test
chmod +x src/lib/dns/gen-rdatacode.py
chmod +x src/lib/dns/tests/testdata/gen-wiredata.py
- chmod +x src/lib/log/tests/run_time_init_test.sh
+ chmod +x src/lib/log/tests/local_file_test.sh
+ chmod +x src/lib/log/tests/console_test.sh
+ chmod +x src/lib/log/tests/destination_test.sh
+ chmod +x src/lib/log/tests/severity_test.sh
chmod +x src/lib/util/python/mkpywrapper.py
chmod +x tests/system/conf.sh
])
@@ -902,6 +957,8 @@ dnl includes too
Boost: ${BOOST_INCLUDES}
Botan: ${BOTAN_INCLUDES}
${BOTAN_LDFLAGS}
+ Log4cplus: ${LOG4CPLUS_INCLUDES}
+ ${LOG4CPLUS_LDFLAGS}
SQLite: $SQLITE_CFLAGS
$SQLITE_LIBS
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am
index 56dc348..9c52504 100644
--- a/src/bin/auth/Makefile.am
+++ b/src/bin/auth/Makefile.am
@@ -51,7 +51,7 @@ b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.la
b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
b10_auth_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-b10_auth_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+b10_auth_LDADD += $(top_builddir)/src/lib/log/liblog.la
b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
b10_auth_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
b10_auth_LDADD += $(SQLITE_LIBS)
diff --git a/src/bin/auth/auth_config.cc b/src/bin/auth/auth_config.cc
index 7929d80..2943cb5 100644
--- a/src/bin/auth/auth_config.cc
+++ b/src/bin/auth/auth_config.cc
@@ -60,6 +60,15 @@ private:
set<string> configured_sources_;
};
+/// A derived \c AuthConfigParser for the version value
+/// (which is not used at this moment)
+class VersionConfig : public AuthConfigParser {
+public:
+ VersionConfig() {}
+ virtual void build(ConstElementPtr) {};
+ virtual void commit() {};
+};
+
void
DatasourcesConfig::build(ConstElementPtr config_value) {
BOOST_FOREACH(ConstElementPtr datasrc_elem, config_value->listValue()) {
@@ -293,6 +302,11 @@ createAuthConfigParser(AuthSrv& server, const std::string& config_id,
// we may introduce dynamic registration of configuration parsers,
// and then this test can be done in a cleaner and safer way.
return (new ThrowerCommitConfig());
+ } else if (config_id == "version") {
+ // Currently, the version identifier is ignored, but it should
+ // later be used to mark backwards incompatible changes in the
+ // config data
+ return (new VersionConfig());
} else {
isc_throw(AuthConfigError, "Unknown configuration identifier: " <<
config_id);
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index a863ef3..9e01155 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -20,6 +20,7 @@
#include <cassert>
#include <iostream>
#include <vector>
+#include <memory>
#include <boost/bind.hpp>
@@ -43,6 +44,7 @@
#include <dns/rrset.h>
#include <dns/rrttl.h>
#include <dns/message.h>
+#include <dns/tsig.h>
#include <datasrc/query.h>
#include <datasrc/data_source.h>
@@ -73,6 +75,7 @@ using namespace isc::xfr;
using namespace isc::asiolink;
using namespace isc::asiodns;
using namespace isc::server_common::portconfig;
+using boost::shared_ptr;
class AuthSrvImpl {
private:
@@ -85,11 +88,14 @@ public:
isc::data::ConstElementPtr setDbFile(isc::data::ConstElementPtr config);
bool processNormalQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer);
+ OutputBufferPtr buffer,
+ auto_ptr<TSIGContext> tsig_context);
bool processAxfrQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer);
+ OutputBufferPtr buffer,
+ auto_ptr<TSIGContext> tsig_context);
bool processNotify(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer);
+ OutputBufferPtr buffer,
+ auto_ptr<TSIGContext> tsig_context);
IOService io_service_;
@@ -116,6 +122,9 @@ public:
/// Addresses we listen on
AddressList listen_addresses_;
+
+ /// The TSIG keyring
+ const shared_ptr<TSIGKeyRing>* keyring_;
private:
std::string db_file_;
@@ -139,6 +148,7 @@ AuthSrvImpl::AuthSrvImpl(const bool use_cache,
memory_datasrc_class_(RRClass::IN()),
statistics_timer_(io_service_),
counters_(verbose_mode_),
+ keyring_(NULL),
xfrout_connected_(false),
xfrout_client_(xfrout_client)
{
@@ -241,7 +251,9 @@ public:
void
makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
- const Rcode& rcode, const bool verbose_mode)
+ const Rcode& rcode, const bool verbose_mode,
+ std::auto_ptr<TSIGContext> tsig_context =
+ std::auto_ptr<TSIGContext>())
{
// extract the parameters that should be kept.
// XXX: with the current implementation, it's not easy to set EDNS0
@@ -272,7 +284,11 @@ makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
message->setRcode(rcode);
MessageRenderer renderer(*buffer);
- message->toWire(renderer);
+ if (tsig_context.get() != NULL) {
+ message->toWire(renderer, *tsig_context);
+ } else {
+ message->toWire(renderer);
+ }
if (verbose_mode) {
cerr << "[b10-auth] sending an error response (" <<
@@ -446,29 +462,51 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
}
// Perform further protocol-level validation.
+ // TSIG first
+ // If this is set to something, we know we need to answer with TSIG as well
+ std::auto_ptr<TSIGContext> tsig_context;
+ const TSIGRecord* tsig_record(message->getTSIGRecord());
+ TSIGError tsig_error(TSIGError::NOERROR());
+
+ // Do we do TSIG?
+ // The keyring can be null if we're in test
+ if (impl_->keyring_ != NULL && tsig_record != NULL) {
+ tsig_context.reset(new TSIGContext(tsig_record->getName(),
+ tsig_record->getRdata().
+ getAlgorithm(),
+ **impl_->keyring_));
+ tsig_error = tsig_context->verify(tsig_record, io_message.getData(),
+ io_message.getDataSize());
+ }
bool sendAnswer = true;
- if (message->getOpcode() == Opcode::NOTIFY()) {
- sendAnswer = impl_->processNotify(io_message, message, buffer);
+ if (tsig_error != TSIGError::NOERROR()) {
+ makeErrorMessage(message, buffer, tsig_error.toRcode(),
+ impl_->verbose_mode_, tsig_context);
+ } else if (message->getOpcode() == Opcode::NOTIFY()) {
+ sendAnswer = impl_->processNotify(io_message, message, buffer,
+ tsig_context);
} else if (message->getOpcode() != Opcode::QUERY()) {
if (impl_->verbose_mode_) {
cerr << "[b10-auth] unsupported opcode" << endl;
}
makeErrorMessage(message, buffer, Rcode::NOTIMP(),
- impl_->verbose_mode_);
+ impl_->verbose_mode_, tsig_context);
} else if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
makeErrorMessage(message, buffer, Rcode::FORMERR(),
- impl_->verbose_mode_);
+ impl_->verbose_mode_, tsig_context);
} else {
ConstQuestionPtr question = *message->beginQuestion();
const RRType &qtype = question->getType();
if (qtype == RRType::AXFR()) {
- sendAnswer = impl_->processAxfrQuery(io_message, message, buffer);
+ sendAnswer = impl_->processAxfrQuery(io_message, message, buffer,
+ tsig_context);
} else if (qtype == RRType::IXFR()) {
makeErrorMessage(message, buffer, Rcode::NOTIMP(),
- impl_->verbose_mode_);
+ impl_->verbose_mode_, tsig_context);
} else {
- sendAnswer = impl_->processNormalQuery(io_message, message, buffer);
+ sendAnswer = impl_->processNormalQuery(io_message, message, buffer,
+ tsig_context);
}
}
@@ -477,7 +515,8 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
bool
AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer)
+ OutputBufferPtr buffer,
+ auto_ptr<TSIGContext> tsig_context)
{
ConstEDNSPtr remote_edns = message->getEDNS();
const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
@@ -523,7 +562,11 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
const bool udp_buffer =
(io_message.getSocket().getProtocol() == IPPROTO_UDP);
renderer.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
- message->toWire(renderer);
+ if (tsig_context.get() != NULL) {
+ message->toWire(renderer, *tsig_context);
+ } else {
+ message->toWire(renderer);
+ }
if (verbose_mode_) {
cerr << "[b10-auth] sending a response ("
@@ -536,7 +579,8 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
bool
AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer)
+ OutputBufferPtr buffer,
+ auto_ptr<TSIGContext> tsig_context)
{
// Increment query counter.
incCounter(io_message.getSocket().getProtocol());
@@ -545,7 +589,8 @@ AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
if (verbose_mode_) {
cerr << "[b10-auth] AXFR query over UDP isn't allowed" << endl;
}
- makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
+ makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_,
+ tsig_context);
return (true);
}
@@ -572,7 +617,8 @@ AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
cerr << "[b10-auth] Error in handling XFR request: " << err.what()
<< endl;
}
- makeErrorMessage(message, buffer, Rcode::SERVFAIL(), verbose_mode_);
+ makeErrorMessage(message, buffer, Rcode::SERVFAIL(), verbose_mode_,
+ tsig_context);
return (true);
}
@@ -581,7 +627,8 @@ AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
bool
AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer)
+ OutputBufferPtr buffer,
+ std::auto_ptr<TSIGContext> tsig_context)
{
// The incoming notify must contain exactly one question for SOA of the
// zone name.
@@ -590,7 +637,8 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
cerr << "[b10-auth] invalid number of questions in notify: "
<< message->getRRCount(Message::SECTION_QUESTION) << endl;
}
- makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
+ makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_,
+ tsig_context);
return (true);
}
ConstQuestionPtr question = *message->beginQuestion();
@@ -599,7 +647,8 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
cerr << "[b10-auth] invalid question RR type in notify: "
<< question->getType() << endl;
}
- makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
+ makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_,
+ tsig_context);
return (true);
}
@@ -662,7 +711,11 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
message->setRcode(Rcode::NOERROR());
MessageRenderer renderer(*buffer);
- message->toWire(renderer);
+ if (tsig_context.get() != NULL) {
+ message->toWire(renderer, *tsig_context);
+ } else {
+ message->toWire(renderer);
+ }
return (true);
}
@@ -772,3 +825,8 @@ void
AuthSrv::setDNSService(isc::asiodns::DNSService& dnss) {
dnss_ = &dnss;
}
+
+void
+AuthSrv::setTSIGKeyRing(const shared_ptr<TSIGKeyRing>* keyring) {
+ impl_->keyring_ = keyring;
+}
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index 88f00c1..19c97b5 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -44,6 +44,9 @@ class MemoryDataSrc;
namespace xfr {
class AbstractXfroutClient;
}
+namespace dns {
+class TSIGKeyRing;
+}
}
@@ -374,6 +377,14 @@ public:
/// \brief Assign an ASIO DNS Service queue to this Auth object
void setDNSService(isc::asiodns::DNSService& dnss);
+ /// \brief Sets the keyring used for verifying and signing
+ ///
+ /// The parameter is pointer to shared pointer, because the automatic
+ /// reloading routines of tsig keys replace the actual keyring object.
+ /// It is expected the pointer will point to some statically-allocated
+ /// object, it doesn't take ownership of it.
+ void setTSIGKeyRing(const boost::shared_ptr<isc::dns::TSIGKeyRing>*
+ keyring);
private:
AuthSrvImpl* impl_;
diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am
index a569147..77d171f 100644
--- a/src/bin/auth/benchmarks/Makefile.am
+++ b/src/bin/auth/benchmarks/Makefile.am
@@ -26,3 +26,4 @@ query_bench_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
query_bench_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
query_bench_LDADD += $(SQLITE_LIBS)
+
diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc
index 480c2f7..0324c6e 100644
--- a/src/bin/auth/main.cc
+++ b/src/bin/auth/main.cc
@@ -47,6 +47,7 @@
#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
#include <log/dummylog.h>
+#include <server_common/keyring.h>
using namespace std;
using namespace isc::data;
@@ -152,9 +153,14 @@ main(int argc, char* argv[]) {
cc_session = new Session(io_service.get_io_service());
cout << "[b10-auth] Configuration session channel created." << endl;
+ // We delay starting listening to new commands/config just before we
+ // go into the main loop to avoid confusion due to mixture of
+ // synchronous and asynchronous operations (this would happen in
+ // initializing TSIG keys below). Until then all operations on the
+ // CC session will take place synchronously.
config_session = new ModuleCCSession(specfile, *cc_session,
my_config_handler,
- my_command_handler);
+ my_command_handler, false);
cout << "[b10-auth] Configuration channel established." << endl;
xfrin_session = new Session(io_service.get_io_service());
@@ -190,6 +196,14 @@ main(int argc, char* argv[]) {
changeUser(uid);
}
+ cout << "[b10-auth] Loading TSIG keys" << endl;
+ isc::server_common::initKeyring(*config_session);
+ auth_server->setTSIGKeyRing(&isc::server_common::keyring);
+
+ // Now start asynchronous read.
+ config_session->start();
+ cout << "[b10-auth] Configuration channel started." << endl;
+
cout << "[b10-auth] Server started." << endl;
io_service.run();
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index 050373a..a4620f5 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -52,6 +52,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index a77f7e6..d922901 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -16,6 +16,8 @@
#include <vector>
+#include <boost/shared_ptr.hpp>
+
#include <gtest/gtest.h>
#include <dns/message.h>
@@ -25,8 +27,10 @@
#include <dns/rrtype.h>
#include <dns/rrttl.h>
#include <dns/rdataclass.h>
+#include <dns/tsig.h>
#include <server_common/portconfig.h>
+#include <server_common/keyring.h>
#include <datasrc/memory_datasrc.h>
#include <auth/auth_srv.h>
@@ -50,6 +54,7 @@ using namespace isc::asiolink;
using namespace isc::testutils;
using namespace isc::server_common::portconfig;
using isc::UnitTestUtil;
+using boost::shared_ptr;
namespace {
const char* const CONFIG_TESTDB =
@@ -242,6 +247,139 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
EXPECT_TRUE(xfrout.isConnected());
}
+// Try giving the server a TSIG signed request and see it can anwer signed as
+// well
+TEST_F(AuthSrvTest, TSIGSigned) {
+ // Prepare key, the client message, etc
+ const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
+ TSIGContext context(key);
+ UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+ Name("version.bind"), RRClass::CH(), RRType::TXT());
+ createRequestPacket(request_message, IPPROTO_UDP, &context);
+
+ // Run the message through the server
+ shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+ keyring->add(key);
+ server.setTSIGKeyRing(&keyring);
+ server.processMessage(*io_message, parse_message, response_obuffer,
+ &dnsserv);
+
+ // What did we get?
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+ opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
+ // We need to parse the message ourself, or getTSIGRecord won't work
+ InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
+ Message m(Message::PARSE);
+ m.fromWire(ib);
+
+ const TSIGRecord* tsig = m.getTSIGRecord();
+ ASSERT_TRUE(tsig != NULL) << "Missing TSIG signature";
+ TSIGError error(context.verify(tsig, response_obuffer->getData(),
+ response_obuffer->getLength()));
+ EXPECT_EQ(TSIGError::NOERROR(), error) <<
+ "The server signed the response, but it doesn't seem to be valid";
+}
+
+// Give the server a signed request, but don't give it the key. It will
+// not be able to verify it, returning BADKEY
+TEST_F(AuthSrvTest, TSIGSignedBadKey) {
+ TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
+ TSIGContext context(key);
+ UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+ Name("version.bind"), RRClass::CH(), RRType::TXT());
+ createRequestPacket(request_message, IPPROTO_UDP, &context);
+
+ // Process the message, but use a different key there
+ shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+ server.setTSIGKeyRing(&keyring);
+ server.processMessage(*io_message, parse_message, response_obuffer,
+ &dnsserv);
+
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, TSIGError::BAD_KEY().toRcode(),
+ opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+ // We need to parse the message ourself, or getTSIGRecord won't work
+ InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
+ Message m(Message::PARSE);
+ m.fromWire(ib);
+
+ const TSIGRecord* tsig = m.getTSIGRecord();
+ ASSERT_TRUE(tsig != NULL) <<
+ "Missing TSIG signature (we should have one even at error)";
+ EXPECT_EQ(TSIGError::BAD_KEY_CODE, tsig->getRdata().getError());
+ EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
+ "It should be unsigned with this error";
+}
+
+// Give the server a signed request, but signed by a different key
+// (with the same name). It should return BADSIG
+TEST_F(AuthSrvTest, TSIGBadSig) {
+ TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
+ TSIGContext context(key);
+ UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+ Name("version.bind"), RRClass::CH(), RRType::TXT());
+ createRequestPacket(request_message, IPPROTO_UDP, &context);
+
+ // Process the message, but use a different key there
+ shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+ keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
+ server.setTSIGKeyRing(&keyring);
+ server.processMessage(*io_message, parse_message, response_obuffer,
+ &dnsserv);
+
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, TSIGError::BAD_SIG().toRcode(),
+ opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+ // We need to parse the message ourself, or getTSIGRecord won't work
+ InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
+ Message m(Message::PARSE);
+ m.fromWire(ib);
+
+ const TSIGRecord* tsig = m.getTSIGRecord();
+ ASSERT_TRUE(tsig != NULL) <<
+ "Missing TSIG signature (we should have one even at error)";
+ EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
+ EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
+ "It should be unsigned with this error";
+}
+
+// Give the server a signed unsupported request with a bad signature.
+// This checks the server first verifies the signature before anything
+// else.
+TEST_F(AuthSrvTest, TSIGCheckFirst) {
+ TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
+ TSIGContext context(key);
+ // Pass a wrong opcode there. The server shouldn't know what to do
+ // about it.
+ UnitTestUtil::createRequestMessage(request_message, Opcode::RESERVED14(),
+ default_qid, Name("version.bind"),
+ RRClass::CH(), RRType::TXT());
+ createRequestPacket(request_message, IPPROTO_UDP, &context);
+
+ // Process the message, but use a different key there
+ shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+ keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
+ server.setTSIGKeyRing(&keyring);
+ server.processMessage(*io_message, parse_message, response_obuffer,
+ &dnsserv);
+
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, TSIGError::BAD_SIG().toRcode(),
+ Opcode::RESERVED14().getCode(), QR_FLAG, 0, 0, 0, 0);
+ // We need to parse the message ourself, or getTSIGRecord won't work
+ InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
+ Message m(Message::PARSE);
+ m.fromWire(ib);
+
+ const TSIGRecord* tsig = m.getTSIGRecord();
+ ASSERT_TRUE(tsig != NULL) <<
+ "Missing TSIG signature (we should have one even at error)";
+ EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
+ EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
+ "It should be unsigned with this error";
+}
+
TEST_F(AuthSrvTest, AXFRConnectFail) {
EXPECT_FALSE(xfrout.isConnected()); // check prerequisite
xfrout.disableConnect();
diff --git a/src/bin/auth/tests/config_unittest.cc b/src/bin/auth/tests/config_unittest.cc
index 7658b84..0890c55 100644
--- a/src/bin/auth/tests/config_unittest.cc
+++ b/src/bin/auth/tests/config_unittest.cc
@@ -74,6 +74,13 @@ TEST_F(AuthConfigTest, databaseConfig) {
"{\"database_file\": \"should_be_ignored\"}")));
}
+TEST_F(AuthConfigTest, versionConfig) {
+ // make sure it does not throw on 'version'
+ EXPECT_NO_THROW(configureAuthServer(
+ server,
+ Element::fromJSON("{\"version\": 0}")));
+}
+
TEST_F(AuthConfigTest, exceptionGuarantee) {
EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
// This configuration contains an invalid item, which will trigger
diff --git a/src/bin/auth/tests/run_unittests.cc b/src/bin/auth/tests/run_unittests.cc
index 6ae848d..d3bbab7 100644
--- a/src/bin/auth/tests/run_unittests.cc
+++ b/src/bin/auth/tests/run_unittests.cc
@@ -13,6 +13,8 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <log/logger_support.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
@@ -21,6 +23,7 @@ main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
+ isc::log::initLogger();
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/bin/cfgmgr/plugins/Makefile.am b/src/bin/cfgmgr/plugins/Makefile.am
index d83c2bb..64f9dc3 100644
--- a/src/bin/cfgmgr/plugins/Makefile.am
+++ b/src/bin/cfgmgr/plugins/Makefile.am
@@ -1,5 +1,6 @@
SUBDIRS = tests
EXTRA_DIST = README tsig_keys.py tsig_keys.spec
+EXTRA_DIST += logging.spec b10logging.py
config_plugindir = @prefix@/share/@PACKAGE@/config_plugins
config_plugin_DATA = tsig_keys.py tsig_keys.spec
diff --git a/src/bin/cfgmgr/plugins/b10logging.py b/src/bin/cfgmgr/plugins/b10logging.py
new file mode 100644
index 0000000..6af3f66
--- /dev/null
+++ b/src/bin/cfgmgr/plugins/b10logging.py
@@ -0,0 +1,96 @@
+# Copyright (C) 2011 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# This is the configuration plugin for logging options
+# The name is 'b10logging' because logging.py is an existing module
+#
+# For a technical background, see
+# http://bind10.isc.org/wiki/LoggingCppApiDesign
+#
+
+from isc.config.module_spec import module_spec_from_file
+from isc.util.file import path_search
+from bind10_config import PLUGIN_PATHS
+spec = module_spec_from_file(path_search('logging.spec', PLUGIN_PATHS))
+
+ALLOWED_SEVERITIES = [ 'default',
+ 'debug',
+ 'info',
+ 'warn',
+ 'error',
+ 'fatal',
+ 'none' ]
+ALLOWED_DESTINATIONS = [ 'console',
+ 'file',
+ 'syslog' ]
+ALLOWED_STREAMS = [ 'stdout',
+ 'stderr' ]
+
+def check(config):
+ # Check the data layout first
+ errors=[]
+ if not spec.validate_config(False, config, errors):
+ return ' '.join(errors)
+ # The 'layout' is ok, now check for specific values
+ if 'loggers' in config:
+ for logger in config['loggers']:
+ # name should always be present
+ name = logger['name']
+
+ if 'severity' in logger and\
+ logger['severity'].lower() not in ALLOWED_SEVERITIES:
+ errors.append("bad severity value for logger " + name +
+ ": " + logger['severity'])
+ if 'output_options' in logger:
+ for output_option in logger['output_options']:
+ if 'destination' in output_option:
+ destination = output_option['destination'].lower()
+ if destination not in ALLOWED_DESTINATIONS:
+ errors.append("bad destination for logger " +
+ name + ": " + output_option['destination'])
+ else:
+ # if left to default, output is stdout, and
+ # it will not show in the updated config,
+ # so 1. we only need to check it if present,
+ # and 2. if destination is changed, so should
+ # output. So first check checks 'in', and the
+ # others 'not in' for 'output'
+ if destination == "console" and\
+ 'output' in output_option and\
+ output_option['output'] not in ALLOWED_STREAMS:
+ errors.append("bad output for logger " + name +
+ ": " + output_option['stream'] +
+ ", must be stdout or stderr")
+ elif destination == "file" and\
+ 'output' not in output_option or\
+ output_option['output'] == "":
+ errors.append("destination set to file but "
+ "output not set to any "
+ "filename for logger "
+ + name)
+ elif destination == "syslog" and\
+ 'output' not in output_option or\
+ output_option['output'] == "":
+ errors.append("destination set to syslog but "
+ "output not set to any facility"
+ " for logger " + name)
+
+ if errors:
+ return ', '.join(errors)
+ return None
+
+def load():
+ return (spec, check)
+
diff --git a/src/bin/cfgmgr/plugins/logging.spec b/src/bin/cfgmgr/plugins/logging.spec
new file mode 100644
index 0000000..e377b0e
--- /dev/null
+++ b/src/bin/cfgmgr/plugins/logging.spec
@@ -0,0 +1,81 @@
+{
+ "module_spec": {
+ "module_name": "Logging",
+ "module_description": "Logging options",
+ "config_data": [
+ {
+ "item_name": "loggers",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "logger",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "name",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ { "item_name": "severity",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "INFO"
+ },
+ { "item_name": "debuglevel",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0
+ },
+ { "item_name": "additive",
+ "item_type": "boolean",
+ "item_optional": false,
+ "item_default": false
+ },
+ { "item_name": "output_options",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "output_option",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "destination",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "console"
+ },
+ { "item_name": "output",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "stdout"
+ },
+ { "item_name": "flush",
+ "item_type": "boolean",
+ "item_optional": false,
+ "item_default": false
+ },
+ { "item_name": "maxsize",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0
+ },
+ { "item_name": "maxver",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ],
+ "commands": []
+ }
+}
diff --git a/src/bin/cfgmgr/plugins/tests/tsig_keys_test.py b/src/bin/cfgmgr/plugins/tests/tsig_keys_test.py
index be2921c..808f28a 100644
--- a/src/bin/cfgmgr/plugins/tests/tsig_keys_test.py
+++ b/src/bin/cfgmgr/plugins/tests/tsig_keys_test.py
@@ -86,7 +86,7 @@ class TSigKeysTest(unittest.TestCase):
self.assertEqual("TSIG: Invalid TSIG key string: invalid.key",
tsig_keys.check({'keys': ['invalid.key']}))
self.assertEqual(
- "TSIG: attempt to decode a value not in base64 char set",
+ "TSIG: Unexpected end of input in BASE decoder",
tsig_keys.check({'keys': ['invalid.key:123']}))
def test_bad_format(self):
diff --git a/src/bin/cmdctl/Makefile.am b/src/bin/cmdctl/Makefile.am
index 04cf5e2..33f8b76 100644
--- a/src/bin/cmdctl/Makefile.am
+++ b/src/bin/cmdctl/Makefile.am
@@ -40,12 +40,13 @@ b10-cmdctl: cmdctl.py
if INSTALL_CONFIGURATIONS
-# TODO: permissions handled later
+# Below we intentionally use ${INSTALL} -m 640 instead of $(INSTALL_DATA)
+# because these file will contain sensitive information.
install-data-local:
$(mkinstalldirs) $(DESTDIR)/@sysconfdir@/@PACKAGE@
for f in $(CMDCTL_CONFIGURATIONS) ; do \
if test ! -f $(DESTDIR)$(sysconfdir)/@PACKAGE@/$$f; then \
- $(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ; \
+ ${INSTALL} -m 640 $(srcdir)/$$f $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ; \
fi ; \
done
diff --git a/src/bin/resolver/Makefile.am b/src/bin/resolver/Makefile.am
index 094e3ad..e826081 100644
--- a/src/bin/resolver/Makefile.am
+++ b/src/bin/resolver/Makefile.am
@@ -18,10 +18,12 @@ endif
pkglibexecdir = $(libexecdir)/@PACKAGE@
-CLEANFILES = *.gcno *.gcda resolver.spec spec_config.h
+CLEANFILES = *.gcno *.gcda
+CLEANFILES += resolver.spec spec_config.h
+CLEANFILES += resolverdef.cc resolverdef.h
man_MANS = b10-resolver.8
-EXTRA_DIST = $(man_MANS) b10-resolver.xml
+EXTRA_DIST = $(man_MANS) b10-resolver.xml resolverdef.mes
if ENABLE_MAN
@@ -36,13 +38,24 @@ resolver.spec: resolver.spec.pre
spec_config.h: spec_config.h.pre
$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
-BUILT_SOURCES = spec_config.h
+# Define rule to build logging source files from message file
+resolverdef.h resolverdef.cc: resolverdef.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/resolver/resolverdef.mes
+
+
+BUILT_SOURCES = spec_config.h resolverdef.cc resolverdef.h
+
pkglibexec_PROGRAMS = b10-resolver
b10_resolver_SOURCES = resolver.cc resolver.h
+b10_resolver_SOURCES += resolver_log.cc resolver_log.h
b10_resolver_SOURCES += response_scrubber.cc response_scrubber.h
b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/change_user.h
b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/common.h
b10_resolver_SOURCES += main.cc
+
+nodist_b10_resolver_SOURCES = resolverdef.cc resolverdef.h
+
+
b10_resolver_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
b10_resolver_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
b10_resolver_LDADD += $(top_builddir)/src/lib/cc/libcc.la
diff --git a/src/bin/resolver/main.cc b/src/bin/resolver/main.cc
index 5103bf9..530f689 100644
--- a/src/bin/resolver/main.cc
+++ b/src/bin/resolver/main.cc
@@ -52,13 +52,14 @@
#include <cache/resolver_cache.h>
#include <nsas/nameserver_address_store.h>
-#include <log/dummylog.h>
+#include <log/logger_support.h>
+#include <log/logger_level.h>
+#include "resolver_log.h"
using namespace std;
using namespace isc::cc;
using namespace isc::config;
using namespace isc::data;
-using isc::log::dlog;
using namespace isc::asiodns;
using namespace isc::asiolink;
@@ -79,7 +80,7 @@ my_command_handler(const string& command, ConstElementPtr args) {
ConstElementPtr answer = createAnswer();
if (command == "print_message") {
- cout << args << endl;
+ LOG_INFO(resolver_logger, RESOLVER_PRINTMSG).arg(args);
/* let's add that message to our answer as well */
answer = createAnswer(0, args);
} else if (command == "shutdown") {
@@ -100,7 +101,7 @@ usage() {
int
main(int argc, char* argv[]) {
- isc::log::dprefix = "b10-resolver";
+ bool verbose = false;
int ch;
const char* uid = NULL;
@@ -110,7 +111,7 @@ main(int argc, char* argv[]) {
uid = optarg;
break;
case 'v':
- isc::log::denabled = true;
+ verbose = true;
break;
case '?':
default:
@@ -122,13 +123,18 @@ main(int argc, char* argv[]) {
usage();
}
- if (isc::log::denabled) { // Show the command line
- string cmdline("Command line:");
- for (int i = 0; i < argc; ++ i) {
- cmdline = cmdline + " " + argv[i];
- }
- dlog(cmdline);
+ // Until proper logging comes along, initialize the logging with the
+ // temporary initLogger() code. If verbose, we'll use maximum verbosity.
+ isc::log::initLogger("b10-resolver",
+ (verbose ? isc::log::DEBUG : isc::log::INFO),
+ isc::log::MAX_DEBUG_LEVEL, NULL);
+
+ // Print the starting message
+ string cmdline = argv[0];
+ for (int i = 1; i < argc; ++ i) {
+ cmdline = cmdline + " " + argv[i];
}
+ LOG_INFO(resolver_logger, RESOLVER_STARTING).arg(cmdline);
int ret = 0;
@@ -144,7 +150,7 @@ main(int argc, char* argv[]) {
}
resolver = boost::shared_ptr<Resolver>(new Resolver());
- dlog("Server created.");
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_CREATED);
SimpleCallback* checkin = resolver->getCheckinProvider();
DNSLookup* lookup = resolver->getDNSLookupProvider();
@@ -197,15 +203,14 @@ main(int argc, char* argv[]) {
DNSService dns_service(io_service, checkin, lookup, answer);
resolver->setDNSService(dns_service);
- dlog("IOService created.");
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_SERVICE);
cc_session = new Session(io_service.get_io_service());
- dlog("Configuration session channel created.");
-
config_session = new ModuleCCSession(specfile, *cc_session,
my_config_handler,
- my_command_handler);
- dlog("Configuration channel established.");
+ my_command_handler,
+ true, true);
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_CONFIGCHAN);
// FIXME: This does not belong here, but inside Boss
if (uid != NULL) {
@@ -213,17 +218,18 @@ main(int argc, char* argv[]) {
}
resolver->setConfigSession(config_session);
- dlog("Config loaded");
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_CONFIGLOAD);
- dlog("Server started.");
+ LOG_INFO(resolver_logger, RESOLVER_STARTED);
io_service.run();
} catch (const std::exception& ex) {
- dlog(string("Server failed: ") + ex.what(),true);
+ LOG_FATAL(resolver_logger, RESOLVER_FAILED).arg(ex.what());
ret = 1;
}
delete config_session;
delete cc_session;
+ LOG_INFO(resolver_logger, RESOLVER_SHUTDOWN);
return (ret);
}
diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc
index e43b48e..934fbdf 100644
--- a/src/bin/resolver/resolver.cc
+++ b/src/bin/resolver/resolver.cc
@@ -45,9 +45,8 @@
#include <resolve/recursive_query.h>
-#include <log/dummylog.h>
-
-#include <resolver/resolver.h>
+#include "resolver.h"
+#include "resolver_log.h"
using namespace std;
@@ -56,7 +55,6 @@ using namespace isc::util;
using namespace isc::dns;
using namespace isc::data;
using namespace isc::config;
-using isc::log::dlog;
using namespace isc::asiodns;
using namespace isc::asiolink;
using namespace isc::server_common::portconfig;
@@ -85,7 +83,7 @@ public:
isc::cache::ResolverCache& cache)
{
assert(!rec_query_); // queryShutdown must be called first
- dlog("Query setup");
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_QUSETUP);
rec_query_ = new RecursiveQuery(dnss,
nsas, cache,
upstream_,
@@ -101,7 +99,7 @@ public:
// (this is not a safety check, just to prevent logging of
// actions that are not performed
if (rec_query_) {
- dlog("Query shutdown");
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_QUSHUT);
delete rec_query_;
rec_query_ = NULL;
}
@@ -113,13 +111,12 @@ public:
upstream_ = upstream;
if (dnss) {
if (!upstream_.empty()) {
- dlog("Setting forward addresses:");
BOOST_FOREACH(const AddressPair& address, upstream) {
- dlog(" " + address.first + ":" +
- boost::lexical_cast<string>(address.second));
+ LOG_INFO(resolver_logger, RESOLVER_FWDADDR)
+ .arg(address.first).arg(address.second);
}
} else {
- dlog("No forward addresses, running in recursive mode");
+ LOG_INFO(resolver_logger, RESOLVER_RECURSIVE);
}
}
}
@@ -130,13 +127,12 @@ public:
upstream_root_ = upstream_root;
if (dnss) {
if (!upstream_root_.empty()) {
- dlog("Setting root addresses:");
BOOST_FOREACH(const AddressPair& address, upstream_root) {
- dlog(" " + address.first + ":" +
- boost::lexical_cast<string>(address.second));
+ LOG_INFO(resolver_logger, RESOLVER_ROOTADDR)
+ .arg(address.first).arg(address.second);
}
} else {
- dlog("No root addresses");
+ LOG_WARN(resolver_logger, RESOLVER_NOROOTADDR);
}
}
}
@@ -186,8 +182,6 @@ class QuestionInserter {
public:
QuestionInserter(MessagePtr message) : message_(message) {}
void operator()(const QuestionPtr question) {
- dlog(string("Adding question ") + question->getName().toText() +
- " to message");
message_->addQuestion(question);
}
MessagePtr message_;
@@ -234,10 +228,6 @@ makeErrorMessage(MessagePtr message, MessagePtr answer_message,
message->setRcode(rcode);
MessageRenderer renderer(*buffer);
message->toWire(renderer);
-
- dlog(string("Sending an error response (") +
- boost::lexical_cast<string>(renderer.getLength()) + " bytes):\n" +
- message->toText());
}
// This is a derived class of \c DNSLookup, to serve as a
@@ -312,9 +302,8 @@ public:
answer_message->toWire(renderer);
- dlog(string("sending a response (") +
- boost::lexical_cast<string>(renderer.getLength()) + "bytes): \n" +
- answer_message->toText());
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_DETAIL, RESOLVER_DNSMSGSENT)
+ .arg(renderer.getLength()).arg(*answer_message);
}
};
@@ -335,9 +324,12 @@ private:
Resolver::Resolver() :
impl_(new ResolverImpl()),
+ dnss_(NULL),
checkin_(new ConfigCheck(this)),
dns_lookup_(new MessageLookup(this)),
dns_answer_(new MessageAnswer),
+ nsas_(NULL),
+ cache_(NULL),
configured_(false)
{}
@@ -391,21 +383,26 @@ Resolver::processMessage(const IOMessage& io_message,
OutputBufferPtr buffer,
DNSServer* server)
{
- dlog("Got a DNS message");
InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
// First, check the header part. If we fail even for the base header,
// just drop the message.
+
+ // In the following code, the debug output is such that there should only be
+ // one debug message if packet processing failed. There could be two if
+ // it succeeded.
try {
query_message->parseHeader(request_buffer);
// Ignore all responses.
if (query_message->getHeaderFlag(Message::HEADERFLAG_QR)) {
- dlog("Received unexpected response, ignoring");
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_UNEXRESP);
server->resume(false);
return;
}
+
} catch (const Exception& ex) {
- dlog(string("DNS packet exception: ") + ex.what(),true);
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_HDRERR)
+ .arg(ex.what());
server->resume(false);
return;
}
@@ -414,37 +411,49 @@ Resolver::processMessage(const IOMessage& io_message,
try {
query_message->fromWire(request_buffer);
} catch (const DNSProtocolError& error) {
- dlog(string("returning ") + error.getRcode().toText() + ": " +
- error.what());
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_PROTERR)
+ .arg(error.what()).arg(error.getRcode());
makeErrorMessage(query_message, answer_message,
buffer, error.getRcode());
server->resume(true);
return;
} catch (const Exception& ex) {
- dlog(string("returning SERVFAIL: ") + ex.what());
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_PROTERR)
+ .arg(ex.what()).arg(Rcode::SERVFAIL());
makeErrorMessage(query_message, answer_message,
buffer, Rcode::SERVFAIL());
server->resume(true);
return;
- } // other exceptions will be handled at a higher layer.
+ } // Other exceptions will be handled at a higher layer.
- dlog("received a message:\n" + query_message->toText());
+ // Note: there appears to be no LOG_DEBUG for a successfully-received
+ // message. This is not an oversight - it is handled below. In the
+ // meantime, output the full message for debug purposes (if requested).
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_DETAIL, RESOLVER_DNSMSGRCVD)
+ .arg(*query_message);
// Perform further protocol-level validation.
bool sendAnswer = true;
if (query_message->getOpcode() == Opcode::NOTIFY()) {
+
makeErrorMessage(query_message, answer_message,
buffer, Rcode::NOTAUTH());
- dlog("Notify arrived, but we are not authoritative");
+ // Notify arrived, but we are not authoritative.
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_NFYNOTAUTH);
+
} else if (query_message->getOpcode() != Opcode::QUERY()) {
- dlog("Unsupported opcode (got: " + query_message->getOpcode().toText() +
- ", expected: " + Opcode::QUERY().toText());
+
+ // Unsupported opcode.
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_OPCODEUNS)
+ .arg(query_message->getOpcode());
makeErrorMessage(query_message, answer_message,
buffer, Rcode::NOTIMP());
+
} else if (query_message->getRRCount(Message::SECTION_QUESTION) != 1) {
- dlog("The query contained " +
- boost::lexical_cast<string>(query_message->getRRCount(
- Message::SECTION_QUESTION) + " questions, exactly one expected"));
+
+ // Not one question
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_NOTONEQUES)
+ .arg(query_message->getRRCount(Message::SECTION_QUESTION));
makeErrorMessage(query_message, answer_message,
buffer, Rcode::FORMERR());
} else {
@@ -452,16 +461,32 @@ Resolver::processMessage(const IOMessage& io_message,
const RRType &qtype = question->getType();
if (qtype == RRType::AXFR()) {
if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
+
+ // Can't process AXFR request receoved over UDP
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS,
+ RESOLVER_AXFRUDP);
makeErrorMessage(query_message, answer_message,
buffer, Rcode::FORMERR());
} else {
+
+ // ... or over TCP for that matter
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS,
+ RESOLVER_AXFRTCP);
makeErrorMessage(query_message, answer_message,
buffer, Rcode::NOTIMP());
}
} else if (qtype == RRType::IXFR()) {
+
+ // Can't process IXFR request
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_IXFR);
makeErrorMessage(query_message, answer_message,
buffer, Rcode::NOTIMP());
+
} else if (question->getClass() != RRClass::IN()) {
+
+ // Non-IN message received, refuse it.
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_NOTIN)
+ .arg(question->getClass());
makeErrorMessage(query_message, answer_message,
buffer, Rcode::REFUSED());
} else {
@@ -492,18 +517,23 @@ ResolverImpl::processNormalQuery(ConstMessagePtr query_message,
DNSServer* server)
{
if (upstream_.empty()) {
- dlog("Processing normal query");
+ // Processing normal query
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_NORMQUERY);
ConstQuestionPtr question = *query_message->beginQuestion();
rec_query_->resolve(*question, answer_message, buffer, server);
+
} else {
- dlog("Processing forward query");
+
+ // Processing forward query
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_FWDQUERY);
rec_query_->forward(query_message, answer_message, buffer, server);
}
}
ConstElementPtr
Resolver::updateConfig(ConstElementPtr config) {
- dlog("New config comes: " + config->toWire());
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_CONFIG, RESOLVER_CONFIGUPD)
+ .arg(*config);
try {
// Parse forward_addresses
@@ -530,6 +560,7 @@ Resolver::updateConfig(ConstElementPtr config) {
// check for us
qtimeout = qtimeoutE->intValue();
if (qtimeout < -1) {
+ LOG_ERROR(resolver_logger, RESOLVER_QUTMOSMALL).arg(qtimeout);
isc_throw(BadValue, "Query timeout too small");
}
set_timeouts = true;
@@ -537,6 +568,7 @@ Resolver::updateConfig(ConstElementPtr config) {
if (ctimeoutE) {
ctimeout = ctimeoutE->intValue();
if (ctimeout < -1) {
+ LOG_ERROR(resolver_logger, RESOLVER_CLTMOSMALL).arg(ctimeout);
isc_throw(BadValue, "Client timeout too small");
}
set_timeouts = true;
@@ -544,12 +576,18 @@ Resolver::updateConfig(ConstElementPtr config) {
if (ltimeoutE) {
ltimeout = ltimeoutE->intValue();
if (ltimeout < -1) {
+ LOG_ERROR(resolver_logger, RESOLVER_LKTMOSMALL).arg(ltimeout);
isc_throw(BadValue, "Lookup timeout too small");
}
set_timeouts = true;
}
if (retriesE) {
+ // Do the assignment from "retriesE->intValue()" to "retries"
+ // _after_ the comparison (as opposed to before it for the timeouts)
+ // because "retries" is unsigned.
if (retriesE->intValue() < 0) {
+ LOG_ERROR(resolver_logger, RESOLVER_RETRYNEG)
+ .arg(retriesE->intValue());
isc_throw(BadValue, "Negative number of retries");
}
retries = retriesE->intValue();
@@ -591,8 +629,11 @@ Resolver::updateConfig(ConstElementPtr config) {
}
setConfigured();
return (isc::config::createAnswer());
+
} catch (const isc::Exception& error) {
- dlog(string("error in config: ") + error.what(),true);
+
+ // Configuration error
+ LOG_ERROR(resolver_logger, RESOLVER_CONFIGERR).arg(error.what());
return (isc::config::createAnswer(1, error.what()));
}
}
@@ -632,10 +673,10 @@ Resolver::setListenAddresses(const AddressList& addresses) {
void
Resolver::setTimeouts(int query_timeout, int client_timeout,
int lookup_timeout, unsigned retries) {
- dlog("Setting query timeout to " + boost::lexical_cast<string>(query_timeout) +
- ", client timeout to " + boost::lexical_cast<string>(client_timeout) +
- ", lookup timeout to " + boost::lexical_cast<string>(lookup_timeout) +
- " and retry count to " + boost::lexical_cast<string>(retries));
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_CONFIG, RESOLVER_SETPARAM)
+ .arg(query_timeout).arg(client_timeout).arg(lookup_timeout)
+ .arg(retries);
+
impl_->query_timeout_ = query_timeout;
impl_->client_timeout_ = client_timeout;
impl_->lookup_timeout_ = lookup_timeout;
diff --git a/src/bin/resolver/resolver_log.cc b/src/bin/resolver/resolver_log.cc
new file mode 100644
index 0000000..4af0159
--- /dev/null
+++ b/src/bin/resolver/resolver_log.cc
@@ -0,0 +1,19 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// Defines the logger used by the NSAS
+
+#include "resolver_log.h"
+
+isc::log::Logger resolver_logger("resolver");
diff --git a/src/bin/resolver/resolver_log.h b/src/bin/resolver/resolver_log.h
new file mode 100644
index 0000000..63f6abb
--- /dev/null
+++ b/src/bin/resolver/resolver_log.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __RESOLVER_LOG__H
+#define __RESOLVER_LOG__H
+
+#include <log/macros.h>
+#include "resolverdef.h"
+
+/// \brief Resolver Logging
+///
+/// Defines the levels used to output debug messages in the resolver. Note that
+/// higher numbers equate to more verbose (and detailed) output.
+
+// Initialization
+const int RESOLVER_DBG_INIT = 10;
+
+// Configuration messages
+const int RESOLVER_DBG_CONFIG = 30;
+
+// Trace sending and receiving of messages
+const int RESOLVER_DBG_IO = 50;
+
+// Trace processing of messages
+const int RESOLVER_DBG_PROCESS = 70;
+
+// Detailed message information
+const int RESOLVER_DBG_DETAIL = 90;
+
+
+/// \brief Resolver Logger
+///
+/// Define the logger used to log messages. We could define it in multiple
+/// modules, but defining in a single module and linking to it saves time and
+/// space.
+extern isc::log::Logger resolver_logger;
+
+#endif // __RESOLVER_LOG__H
diff --git a/src/bin/resolver/resolverdef.mes b/src/bin/resolver/resolverdef.mes
new file mode 100644
index 0000000..bb89cfa
--- /dev/null
+++ b/src/bin/resolver/resolverdef.mes
@@ -0,0 +1,193 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$PREFIX RESOLVER_
+# No namespace declaration - these constants go in the global namespace
+# along with the resolver methods.
+
+% AXFRTCP AXFR request received over TCP
+A debug message, the resolver received a NOTIFY message over TCP. The server
+cannot process it and will return an error message to the sender with the
+RCODE set to NOTIMP.
+
+% AXFRUDP AXFR request received over UDP
+A debug message, the resolver received a NOTIFY message over UDP. The server
+cannot process it (and in any case, an AXFR request should be sent over TCP)
+and will return an error message to the sender with the RCODE set to FORMERR.
+
+% CONFIGCHAN configuration channel created
+A debug message, output when the resolver has successfully established a
+connection to the configuration channel.
+
+% CONFIGERR error in configuration: %1
+An error was detected in a configuration update received by the resolver. This
+may be in the format of the configuration message (in which case this is a
+programming error) or it may be in the data supplied (in which case it is
+a user error). The reason for the error, given as a parameter in the message,
+will give more details.
+
+% CONFIGLOAD configuration loaded
+A debug message, output when the resolver configuration has been successfully
+loaded.
+
+% CONFIGUPD configuration updated: %1
+A debug message, the configuration has been updated with the specified
+information.
+
+% DNSMSGRCVD DNS message received: %1
+A debug message, this always precedes some other logging message and is the
+formatted contents of the DNS packet that the other message refers to.
+
+% DNSMSGSENT DNS message of %1 bytes sent: %2
+A debug message, this contains details of the response sent back to the querying
+system.
+
+% CLTMOSMALL client timeout of %1 is too small
+An error indicating that the configuration value specified for the query
+timeout is too small.
+
+% CREATED main resolver object created
+A debug message, output when the Resolver() object has been created.
+
+% FAILED resolver failed, reason: %1
+This is an error message output when an unhandled exception is caught by the
+resolver. All it can do is to shut down.
+
+% FWDADDR setting forward address %1(%2)
+This message may appear multiple times during startup, and it lists the
+forward addresses used by the resolver when running in forwarding mode.
+
+% FWDQUERY processing forward query
+The received query has passed all checks and is being forwarded to upstream
+servers.
+
+% HDRERR message received, exception when processing header: %1
+A debug message noting that an exception occurred during the processing of
+a received packet. The packet has been dropped.
+
+% IXFR IXFR request received
+The resolver received a NOTIFY message over TCP. The server cannot process it
+and will return an error message to the sender with the RCODE set to NOTIMP.
+
+% LKTMOSMALL lookup timeout of %1 is too small
+An error indicating that the configuration value specified for the lookup
+timeout is too small.
+
+% NFYNOTAUTH NOTIFY arrived but server is not authoritative
+The resolver received a NOTIFY message. As the server is not authoritative it
+cannot process it, so it returns an error message to the sender with the RCODE
+set to NOTAUTH.
+
+% NORMQUERY processing normal query
+The received query has passed all checks and is being processed by the resolver.
+
+% NOTIN non-IN class request received, returning REFUSED message
+A debug message, the resolver has received a DNS packet that was not IN class.
+The resolver cannot handle such packets, so is returning a REFUSED response to
+the sender.
+
+% NOROOTADDR no root addresses available
+A warning message during startup, indicates that no root addresses have been
+set. This may be because the resolver will get them from a priming query.
+
+% NOTONEQUES query contained %1 questions, exactly one question was expected
+A debug message, the resolver received a query that contained the number of
+entires in the question section detailed in the message. This is a malformed
+message, as a DNS query must contain only one question. The resolver will
+return a message to the sender with the RCODE set to FORMERR.
+
+% OPCODEUNS opcode %1 not supported by the resolver
+A debug message, the resolver received a message with an unsupported opcode
+(it can only process QUERY opcodes). It will return a message to the sender
+with the RCODE set to NOTIMP.
+
+% PARSEERR error parsing received message: %1 - returning %2
+A debug message noting that the resolver received a message and the parsing
+of the body of the message failed due to some non-protocol related reason
+(although the parsing of the header succeeded). The message parameters give
+a textual description of the problem and the RCODE returned.
+
+% PRINTMSG print message command, aeguments are: %1
+This message is logged when a "print_message" command is received over the
+command channel.
+
+% PROTERR protocol error parsing received message: %1 - returning %2
+A debug message noting that the resolver received a message and the parsing
+of the body of the message failed due to some protocol error (although the
+parsing of the header succeeded). The message parameters give a textual
+description of the problem and the RCODE returned.
+
+% QUSETUP query setup
+A debug message noting that the resolver is creating a RecursiveQuery object.
+
+% QUSHUT query shutdown
+A debug message noting that the resolver is destroying a RecursiveQuery object.
+
+% QUTMOSMALL query timeout of %1 is too small
+An error indicating that the configuration value specified for the query
+timeout is too small.
+
+% RECURSIVE running in recursive mode
+This is an informational message that appears at startup noting that the
+resolver is running in recursive mode.
+
+% RECVMSG resolver has received a DNS message
+A debug message indicating that the resolver has received a message. Depending
+on the debug settings, subsequent log output will indicate the nature of the
+message.
+
+% RETRYNEG negative number of retries (%1) specified in the configuration
+An error message indicating that the resolver configuration has specified a
+negative retry count. Only zero or positive values are valid.
+
+% ROOTADDR setting root address %1(%2)
+This message may appear multiple times during startup; it lists the root
+addresses used by the resolver.
+
+% SERVICE service object created
+A debug message, output when the main service object (which handles the
+received queries) is created.
+
+% SETPARAM query timeout: %1, client timeout: %2, lookup timeout: %3, retry count: %4
+A debug message, lists the parameters associated with the message. These are:
+query timeout: the timeout (in ms) used for queries originated by the resolver
+to upstream servers. Client timeout: the interval to resolver a query by
+a client: after this time, the resolver sends back a SERVFAIL to the client
+whilst continuing to resolver the query. Lookup timeout: the time at which the
+resolver gives up trying to resolve a query. Retry count: the number of times
+the resolver will retry a query to an upstream server if it gets a timeout.
+
+The client and lookup timeouts require a bit more explanation. The
+resolution of the clent query might require a large number of queries to
+upstream nameservers. Even if none of these queries timeout, the total time
+taken to perform all the queries may exceed the client timeout. When this
+happens, a SERVFAIL is returned to the client, but the resolver continues
+with the resolution process. Data received is added to the cache. However,
+there comes a time - the lookup timeout - when even the resolve gives up.
+At this point it will wait for pending upstream queries to complete or
+timeout and drop the query.
+
+% SHUTDOWN resolver shutdown complete
+This information message is output when the resolver has shut down.
+
+% STARTED resolver started
+This informational message is output by the resolver when all initialization
+has been completed and it is entering its main loop.
+
+% STARTING starting resolver with command line '%1'
+An informational message, this is output when the resolver starts up.
+
+% UNEXRESP received unexpected response, ignoring
+A debug message noting that the server has received a response instead of a
+query and is ignoring it.
diff --git a/src/bin/resolver/tests/Makefile.am b/src/bin/resolver/tests/Makefile.am
index 444358b..35b5398 100644
--- a/src/bin/resolver/tests/Makefile.am
+++ b/src/bin/resolver/tests/Makefile.am
@@ -1,6 +1,7 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
+AM_CPPFLAGS += -I$(top_builddir)/src/bin/resolver
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(top_srcdir)/src/lib/testutils/testdata\"
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/testutils/testdata\"
AM_CPPFLAGS += $(BOOST_INCLUDES)
@@ -16,20 +17,24 @@ CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
TESTS += run_unittests
+
run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += ../resolver.h ../resolver.cc
+run_unittests_SOURCES += ../resolver_log.h ../resolver_log.cc
run_unittests_SOURCES += ../response_scrubber.h ../response_scrubber.cc
run_unittests_SOURCES += resolver_unittest.cc
run_unittests_SOURCES += resolver_config_unittest.cc
run_unittests_SOURCES += response_scrubber_unittest.cc
run_unittests_SOURCES += run_unittests.cc
+
+nodist_run_unittests_SOURCES = ../resolverdef.h ../resolverdef.cc
+
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(SQLITE_LIBS)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
-run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
@@ -42,6 +47,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
# Note the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS
diff --git a/src/bin/resolver/tests/run_unittests.cc b/src/bin/resolver/tests/run_unittests.cc
index 6ae848d..d3bbab7 100644
--- a/src/bin/resolver/tests/run_unittests.cc
+++ b/src/bin/resolver/tests/run_unittests.cc
@@ -13,6 +13,8 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <log/logger_support.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
@@ -21,6 +23,7 @@ main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
+ isc::log::initLogger();
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/bin/sockcreator/tests/Makefile.am b/src/bin/sockcreator/tests/Makefile.am
index 2e1307a..223e761 100644
--- a/src/bin/sockcreator/tests/Makefile.am
+++ b/src/bin/sockcreator/tests/Makefile.am
@@ -16,10 +16,9 @@ run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
-run_unittests_LDADD += \
- $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/sockcreator/tests/run_unittests.cc b/src/bin/sockcreator/tests/run_unittests.cc
index e787ab1..1287164 100644
--- a/src/bin/sockcreator/tests/run_unittests.cc
+++ b/src/bin/sockcreator/tests/run_unittests.cc
@@ -13,10 +13,11 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+ return isc::util::unittests::run_all();
}
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
index 472ef3c..c0db5c6 100644
--- a/src/bin/xfrout/tests/xfrout_test.py.in
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -18,11 +18,14 @@
import unittest
import os
+from isc.testutils.tsigctx_mock import MockTSIGContext
from isc.cc.session import *
from pydnspp import *
from xfrout import *
import xfrout
+TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==")
+
# our fake socket, where we can read and insert messages
class MySocket():
def __init__(self, family, type):
@@ -85,10 +88,36 @@ class TestXfroutSession(unittest.TestCase):
msg.from_wire(self.mdata)
return msg
+ def create_mock_tsig_ctx(self, error):
+ # This helper function creates a MockTSIGContext for a given key
+ # and TSIG error to be used as a result of verify (normally faked
+ # one)
+ mock_ctx = MockTSIGContext(TSIG_KEY)
+ mock_ctx.error = error
+ return mock_ctx
+
+ def message_has_tsig(self, msg):
+ return msg.get_tsig_record() is not None
+
+ def create_request_data_with_tsig(self):
+ msg = Message(Message.RENDER)
+ query_id = 0x1035
+ msg.set_qid(query_id)
+ msg.set_opcode(Opcode.QUERY())
+ msg.set_rcode(Rcode.NOERROR())
+ query_question = Question(Name("example.com."), RRClass.IN(), RRType.AXFR())
+ msg.add_question(query_question)
+
+ renderer = MessageRenderer()
+ tsig_ctx = MockTSIGContext(TSIG_KEY)
+ msg.to_wire(renderer, tsig_ctx)
+ reply_data = renderer.get_data()
+ return reply_data
+
def setUp(self):
self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM)
self.log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False )
- self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(), self.log)
+ self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(), self.log, TSIGKeyRing())
self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200')
@@ -96,6 +125,18 @@ class TestXfroutSession(unittest.TestCase):
[get_rcode, get_msg] = self.xfrsess._parse_query_message(self.mdata)
self.assertEqual(get_rcode.to_text(), "NOERROR")
+ # tsig signed query message
+ request_data = self.create_request_data_with_tsig()
+ # BADKEY
+ [rcode, msg] = self.xfrsess._parse_query_message(request_data)
+ self.assertEqual(rcode.to_text(), "NOTAUTH")
+ self.assertTrue(self.xfrsess._tsig_ctx is not None)
+ # NOERROR
+ self.xfrsess._tsig_key_ring.add(TSIG_KEY)
+ [rcode, msg] = self.xfrsess._parse_query_message(request_data)
+ self.assertEqual(rcode.to_text(), "NOERROR")
+ self.assertTrue(self.xfrsess._tsig_ctx is not None)
+
def test_get_query_zone_name(self):
msg = self.getmsg()
self.assertEqual(self.xfrsess._get_query_zone_name(msg), "example.com.")
@@ -111,6 +152,14 @@ class TestXfroutSession(unittest.TestCase):
get_msg = self.sock.read_msg()
self.assertEqual(get_msg.get_rcode().to_text(), "NXDOMAIN")
+ # tsig signed message
+ msg = self.getmsg()
+ self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+ self.xfrsess._reply_query_with_error_rcode(msg, self.sock, Rcode(3))
+ get_msg = self.sock.read_msg()
+ self.assertEqual(get_msg.get_rcode().to_text(), "NXDOMAIN")
+ self.assertTrue(self.message_has_tsig(get_msg))
+
def test_send_message(self):
msg = self.getmsg()
msg.make_response()
@@ -152,6 +201,14 @@ class TestXfroutSession(unittest.TestCase):
get_msg = self.sock.read_msg()
self.assertEqual(get_msg.get_rcode().to_text(), "FORMERR")
+ # tsig signed message
+ msg = self.getmsg()
+ self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+ self.xfrsess._reply_query_with_format_error(msg, self.sock)
+ get_msg = self.sock.read_msg()
+ self.assertEqual(get_msg.get_rcode().to_text(), "FORMERR")
+ self.assertTrue(self.message_has_tsig(get_msg))
+
def test_create_rrset_from_db_record(self):
rrset = self.xfrsess._create_rrset_from_db_record(self.soa_record)
self.assertEqual(rrset.get_name().to_text(), "example.com.")
@@ -162,11 +219,16 @@ class TestXfroutSession(unittest.TestCase):
def test_send_message_with_last_soa(self):
rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
-
msg = self.getmsg()
msg.make_response()
- self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, 0)
+
+ # packet number less than TSIG_SIGN_EVERY_NTH
+ packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa,
+ 0, packet_neet_not_sign)
get_msg = self.sock.read_msg()
+ # tsig context is not exist
+ self.assertFalse(self.message_has_tsig(get_msg))
self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
@@ -180,6 +242,42 @@ class TestXfroutSession(unittest.TestCase):
rdata = answer.get_rdata()
self.assertEqual(rdata[0].to_text(), self.soa_record[7])
+ # msg is the TSIG_SIGN_EVERY_NTH one
+ # sending the message with last soa together
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa,
+ 0, TSIG_SIGN_EVERY_NTH)
+ get_msg = self.sock.read_msg()
+ # tsig context is not exist
+ self.assertFalse(self.message_has_tsig(get_msg))
+
+ def test_send_message_with_last_soa_with_tsig(self):
+ # create tsig context
+ self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+
+ rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+ msg = self.getmsg()
+ msg.make_response()
+
+ # packet number less than TSIG_SIGN_EVERY_NTH
+ packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
+ # msg is not the TSIG_SIGN_EVERY_NTH one
+ # sending the message with last soa together
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa,
+ 0, packet_neet_not_sign)
+ get_msg = self.sock.read_msg()
+ self.assertTrue(self.message_has_tsig(get_msg))
+
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
+
+ # msg is the TSIG_SIGN_EVERY_NTH one
+ # sending the message with last soa together
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa,
+ 0, TSIG_SIGN_EVERY_NTH)
+ get_msg = self.sock.read_msg()
+ self.assertTrue(self.message_has_tsig(get_msg))
+
def test_trigger_send_message_with_last_soa(self):
rrset_a = RRset(Name("example.com"), RRClass.IN(), RRType.A(), RRTTL(3600))
rrset_a.add_rdata(Rdata(RRType.A(), RRClass.IN(), "192.0.2.1"))
@@ -187,15 +285,21 @@ class TestXfroutSession(unittest.TestCase):
msg = self.getmsg()
msg.make_response()
-
msg.add_rrset(Message.SECTION_ANSWER, rrset_a)
- # give the function a value that is larger than MAX-len(rrset)
- self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, 65520)
+ # length larger than MAX-len(rrset)
+ length_need_split = xfrout.XFROUT_MAX_MESSAGE_SIZE - get_rrset_len(rrset_soa) + 1
+ # packet number less than TSIG_SIGN_EVERY_NTH
+ packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
+
+ # give the function a value that is larger than MAX-len(rrset)
# this should have triggered the sending of two messages
# (1 with the rrset we added manually, and 1 that triggered
# the sending in _with_last_soa)
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, length_need_split,
+ packet_neet_not_sign)
get_msg = self.sock.read_msg()
+ self.assertFalse(self.message_has_tsig(get_msg))
self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
@@ -208,6 +312,7 @@ class TestXfroutSession(unittest.TestCase):
self.assertEqual(rdata[0].to_text(), "192.0.2.1")
get_msg = self.sock.read_msg()
+ self.assertFalse(self.message_has_tsig(get_msg))
self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 0)
self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
@@ -223,6 +328,45 @@ class TestXfroutSession(unittest.TestCase):
# and it should not have sent anything else
self.assertEqual(0, len(self.sock.sendqueue))
+ def test_trigger_send_message_with_last_soa_with_tsig(self):
+ self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+ rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+ msg = self.getmsg()
+ msg.make_response()
+ msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
+
+ # length larger than MAX-len(rrset)
+ length_need_split = xfrout.XFROUT_MAX_MESSAGE_SIZE - get_rrset_len(rrset_soa) + 1
+ # packet number less than TSIG_SIGN_EVERY_NTH
+ packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
+
+ # give the function a value that is larger than MAX-len(rrset)
+ # this should have triggered the sending of two messages
+ # (1 with the rrset we added manually, and 1 that triggered
+ # the sending in _with_last_soa)
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, length_need_split,
+ packet_neet_not_sign)
+ get_msg = self.sock.read_msg()
+ # msg is not the TSIG_SIGN_EVERY_NTH one, it shouldn't be tsig signed
+ self.assertFalse(self.message_has_tsig(get_msg))
+ # the last packet should be tsig signed
+ get_msg = self.sock.read_msg()
+ self.assertTrue(self.message_has_tsig(get_msg))
+ # and it should not have sent anything else
+ self.assertEqual(0, len(self.sock.sendqueue))
+
+
+ # msg is the TSIG_SIGN_EVERY_NTH one, it should be tsig signed
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, length_need_split,
+ xfrout.TSIG_SIGN_EVERY_NTH)
+ get_msg = self.sock.read_msg()
+ self.assertTrue(self.message_has_tsig(get_msg))
+ # the last packet should be tsig signed
+ get_msg = self.sock.read_msg()
+ self.assertTrue(self.message_has_tsig(get_msg))
+ # and it should not have sent anything else
+ self.assertEqual(0, len(self.sock.sendqueue))
+
def test_get_rrset_len(self):
rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
self.assertEqual(82, get_rrset_len(rrset_soa))
@@ -313,6 +457,51 @@ class TestXfroutSession(unittest.TestCase):
reply_msg = self.sock.read_msg()
self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER), 2)
+ def test_reply_xfrout_query_noerror_with_tsig(self):
+ rrset_data = (4, 3, 'a.example.com.', 'com.example.', 3600, 'A', None, '192.168.1.1')
+ global sqlite3_ds
+ global xfrout
+ def get_zone_soa(zonename, file):
+ return self.soa_record
+
+ def get_zone_datas(zone, file):
+ zone_rrsets = []
+ for i in range(0, 100):
+ zone_rrsets.insert(i, rrset_data)
+ return zone_rrsets
+
+ def get_rrset_len(rrset):
+ return 65520
+
+ sqlite3_ds.get_zone_soa = get_zone_soa
+ sqlite3_ds.get_zone_datas = get_zone_datas
+ xfrout.get_rrset_len = get_rrset_len
+
+ self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+ self.xfrsess._reply_xfrout_query(self.getmsg(), self.sock, "example.com.")
+
+ # tsig signed first package
+ reply_msg = self.sock.read_msg()
+ self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER), 1)
+ self.assertTrue(self.message_has_tsig(reply_msg))
+ # (TSIG_SIGN_EVERY_NTH - 1) packets have no tsig
+ for i in range(0, xfrout.TSIG_SIGN_EVERY_NTH - 1):
+ reply_msg = self.sock.read_msg()
+ self.assertFalse(self.message_has_tsig(reply_msg))
+ # TSIG_SIGN_EVERY_NTH packet has tsig
+ reply_msg = self.sock.read_msg()
+ self.assertTrue(self.message_has_tsig(reply_msg))
+
+ for i in range(0, 100 - TSIG_SIGN_EVERY_NTH):
+ reply_msg = self.sock.read_msg()
+ self.assertFalse(self.message_has_tsig(reply_msg))
+ # tsig signed last package
+ reply_msg = self.sock.read_msg()
+ self.assertTrue(self.message_has_tsig(reply_msg))
+
+ # and it should not have sent anything else
+ self.assertEqual(0, len(self.sock.sendqueue))
+
class MyCCSession():
def __init__(self):
pass
@@ -347,8 +536,23 @@ class TestUnixSockServer(unittest.TestCase):
self.assertEqual(recv_msg, send_msg)
def test_updata_config_data(self):
+ tsig_key_str = 'example.com:SFuWd/q99SzF8Yzd1QbB9g=='
+ tsig_key_list = [tsig_key_str]
+ bad_key_list = ['bad..example.com:SFuWd/q99SzF8Yzd1QbB9g==']
self.unix.update_config_data({'transfers_out':10 })
self.assertEqual(self.unix._max_transfers_out, 10)
+ self.assertTrue(self.unix.tsig_key_ring is not None)
+
+ self.unix.update_config_data({'transfers_out':9, 'tsig_key_ring':tsig_key_list})
+ self.assertEqual(self.unix._max_transfers_out, 9)
+ self.assertEqual(self.unix.tsig_key_ring.size(), 1)
+ self.unix.tsig_key_ring.remove(Name("example.com."))
+ self.assertEqual(self.unix.tsig_key_ring.size(), 0)
+
+ # bad tsig key
+ config_data = {'transfers_out':9, 'tsig_key_ring': bad_key_list}
+ self.assertRaises(None, self.unix.update_config_data(config_data))
+ self.assertEqual(self.unix.tsig_key_ring.size(), 0)
def test_get_db_file(self):
self.assertEqual(self.unix.get_db_file(), "initdb.file")
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index 17ca3eb..f352c74 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -74,10 +74,12 @@ SPECFILE_LOCATION = SPECFILE_PATH + "/xfrout.spec"
AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + os.sep + "auth.spec"
MAX_TRANSFERS_OUT = 10
VERBOSE_MODE = False
-
+# tsig sign every N axfr packets.
+TSIG_SIGN_EVERY_NTH = 96
XFROUT_MAX_MESSAGE_SIZE = 65535
+
def get_rrset_len(rrset):
"""Returns the wire length of the given RRset"""
bytes = bytearray()
@@ -86,15 +88,22 @@ def get_rrset_len(rrset):
class XfroutSession():
- def __init__(self, sock_fd, request_data, server, log):
+ def __init__(self, sock_fd, request_data, server, log, tsig_key_ring):
# The initializer for the superclass may call functions
# that need _log to be set, so we set it first
self._sock_fd = sock_fd
self._request_data = request_data
self._server = server
self._log = log
+ self._tsig_key_ring = tsig_key_ring
+ self._tsig_ctx = None
+ self._tsig_len = 0
self.handle()
+ def create_tsig_ctx(self, tsig_record, tsig_key_ring):
+ return TSIGContext(tsig_record.get_name(), tsig_record.get_rdata().get_algorithm(),
+ tsig_key_ring)
+
def handle(self):
''' Handle a xfrout query, send xfrout response '''
try:
@@ -105,17 +114,33 @@ class XfroutSession():
os.close(self._sock_fd)
+ def _check_request_tsig(self, msg, request_data):
+ ''' If request has a tsig record, perform tsig related checks '''
+ tsig_record = msg.get_tsig_record()
+ if tsig_record is not None:
+ self._tsig_len = tsig_record.get_length()
+ self._tsig_ctx = self.create_tsig_ctx(tsig_record, self._tsig_key_ring)
+ tsig_error = self._tsig_ctx.verify(tsig_record, request_data)
+ if tsig_error != TSIGError.NOERROR:
+ return Rcode.NOTAUTH()
+
+ return Rcode.NOERROR()
+
def _parse_query_message(self, mdata):
''' parse query message to [socket,message]'''
#TODO, need to add parseHeader() in case the message header is invalid
try:
msg = Message(Message.PARSE)
Message.from_wire(msg, mdata)
+
+ # TSIG related checks
+ rcode = self._check_request_tsig(msg, mdata)
+
except Exception as err:
self._log.log_message("error", str(err))
return Rcode.FORMERR(), None
- return Rcode.NOERROR(), msg
+ return rcode, msg
def _get_query_zone_name(self, msg):
question = msg.get_question()[0]
@@ -130,13 +155,20 @@ class XfroutSession():
total_count += count
- def _send_message(self, sock_fd, msg):
+ def _send_message(self, sock_fd, msg, tsig_ctx=None):
render = MessageRenderer()
# As defined in RFC5936 section3.4, perform case-preserving name
# compression for AXFR message.
render.set_compress_mode(MessageRenderer.CASE_SENSITIVE)
render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
- msg.to_wire(render)
+
+ # XXX Currently, python wrapper doesn't accept 'None' parameter in this case,
+ # we should remove the if statement and use a universal interface later.
+ if tsig_ctx is not None:
+ msg.to_wire(render, tsig_ctx)
+ else:
+ msg.to_wire(render)
+
header_len = struct.pack('H', socket.htons(render.get_length()))
self._send_data(sock_fd, header_len)
self._send_data(sock_fd, render.get_data())
@@ -145,7 +177,7 @@ class XfroutSession():
def _reply_query_with_error_rcode(self, msg, sock_fd, rcode_):
msg.make_response()
msg.set_rcode(rcode_)
- self._send_message(sock_fd, msg)
+ self._send_message(sock_fd, msg, self._tsig_ctx)
def _reply_query_with_format_error(self, msg, sock_fd):
@@ -155,7 +187,7 @@ class XfroutSession():
msg.make_response()
msg.set_rcode(Rcode.FORMERR())
- self._send_message(sock_fd, msg)
+ self._send_message(sock_fd, msg, self._tsig_ctx)
def _zone_has_soa(self, zone):
'''Judge if the zone has an SOA record.'''
@@ -204,7 +236,9 @@ class XfroutSession():
def dns_xfrout_start(self, sock_fd, msg_query):
rcode_, msg = self._parse_query_message(msg_query)
#TODO. create query message and parse header
- if rcode_ != Rcode.NOERROR():
+ if rcode_ == Rcode.NOTAUTH():
+ return self._reply_query_with_error_rcode(msg, sock_fd, rcode_)
+ elif rcode_ != Rcode.NOERROR():
return self._reply_query_with_format_error(msg, sock_fd)
zone_name = self._get_query_zone_name(msg)
@@ -248,37 +282,43 @@ class XfroutSession():
rrset_.add_rdata(rdata_)
return rrset_
- def _send_message_with_last_soa(self, msg, sock_fd, rrset_soa, message_upper_len):
+ def _send_message_with_last_soa(self, msg, sock_fd, rrset_soa, message_upper_len,
+ count_since_last_tsig_sign):
'''Add the SOA record to the end of message. If it can't be
added, a new message should be created to send out the last soa .
'''
rrset_len = get_rrset_len(rrset_soa)
- if message_upper_len + rrset_len < XFROUT_MAX_MESSAGE_SIZE:
- msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
- else:
+ if (count_since_last_tsig_sign == TSIG_SIGN_EVERY_NTH and
+ message_upper_len + rrset_len >= XFROUT_MAX_MESSAGE_SIZE):
+ # If tsig context exist, sign the packet with serial number TSIG_SIGN_EVERY_NTH
+ self._send_message(sock_fd, msg, self._tsig_ctx)
+ msg = self._clear_message(msg)
+ elif (count_since_last_tsig_sign != TSIG_SIGN_EVERY_NTH and
+ message_upper_len + rrset_len + self._tsig_len >= XFROUT_MAX_MESSAGE_SIZE):
self._send_message(sock_fd, msg)
msg = self._clear_message(msg)
- msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
- self._send_message(sock_fd, msg)
+ # If tsig context exist, sign the last packet
+ msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
+ self._send_message(sock_fd, msg, self._tsig_ctx)
def _reply_xfrout_query(self, msg, sock_fd, zone_name):
#TODO, there should be a better way to insert rrset.
+ count_since_last_tsig_sign = TSIG_SIGN_EVERY_NTH
msg.make_response()
msg.set_header_flag(Message.HEADERFLAG_AA)
soa_record = sqlite3_ds.get_zone_soa(zone_name, self._server.get_db_file())
rrset_soa = self._create_rrset_from_db_record(soa_record)
msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
- message_upper_len = get_rrset_len(rrset_soa)
+ message_upper_len = get_rrset_len(rrset_soa) + self._tsig_len
for rr_data in sqlite3_ds.get_zone_datas(zone_name, self._server.get_db_file()):
if self._server._shutdown_event.is_set(): # Check if xfrout is shutdown
self._log.log_message("info", "xfrout process is being shutdown")
return
-
# TODO: RRType.SOA() ?
if RRType(rr_data[5]) == RRType("SOA"): #ignore soa record
continue
@@ -294,12 +334,25 @@ class XfroutSession():
message_upper_len += rrset_len
continue
- self._send_message(sock_fd, msg)
+ # If tsig context exist, sign every N packets
+ if count_since_last_tsig_sign == TSIG_SIGN_EVERY_NTH:
+ count_since_last_tsig_sign = 0
+ self._send_message(sock_fd, msg, self._tsig_ctx)
+ else:
+ self._send_message(sock_fd, msg)
+
+ count_since_last_tsig_sign += 1
msg = self._clear_message(msg)
msg.add_rrset(Message.SECTION_ANSWER, rrset_) # Add the rrset to the new message
- message_upper_len = rrset_len
- self._send_message_with_last_soa(msg, sock_fd, rrset_soa, message_upper_len)
+ # Reserve tsig space for signed packet
+ if count_since_last_tsig_sign == TSIG_SIGN_EVERY_NTH:
+ message_upper_len = rrset_len + self._tsig_len
+ else:
+ message_upper_len = rrset_len
+
+ self._send_message_with_last_soa(msg, sock_fd, rrset_soa, message_upper_len,
+ count_since_last_tsig_sign)
class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
'''The unix domain socket server which accept xfr query sent from auth server.'''
@@ -403,7 +456,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
def finish_request(self, sock_fd, request_data):
'''Finish one request by instantiating RequestHandlerClass.'''
- self.RequestHandlerClass(sock_fd, request_data, self, self._log)
+ self.RequestHandlerClass(sock_fd, request_data, self, self._log, self.tsig_key_ring)
def _remove_unused_sock_file(self, sock_file):
'''Try to remove the socket file. If the file is being used
@@ -449,10 +502,27 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
self._log.log_message('info', 'update config data start.')
self._lock.acquire()
self._max_transfers_out = new_config.get('transfers_out')
+ self.set_tsig_key_ring(new_config.get('tsig_key_ring'))
self._log.log_message('info', 'max transfer out : %d', self._max_transfers_out)
self._lock.release()
self._log.log_message('info', 'update config data complete.')
+ def set_tsig_key_ring(self, key_list):
+ """Set the tsig_key_ring , given a TSIG key string list representation. """
+
+ # XXX add values to configure zones/tsig options
+ self.tsig_key_ring = TSIGKeyRing()
+ # If key string list is empty, create a empty tsig_key_ring
+ if not key_list:
+ return
+
+ for key_item in key_list:
+ try:
+ self.tsig_key_ring.add(TSIGKey(key_item))
+ except InvalidParameter as ipe:
+ errmsg = "bad TSIG key string: " + str(key_item)
+ self._log.log_message('error', '%s' % errmsg)
+
def get_db_file(self):
file, is_default = self._cc.get_remote_config_value("Auth", "database_file")
# this too should be unnecessary, but currently the
diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in
index 941db72..2efa3d7 100644
--- a/src/bin/xfrout/xfrout.spec.pre.in
+++ b/src/bin/xfrout/xfrout.spec.pre.in
@@ -37,6 +37,18 @@
"item_type": "integer",
"item_optional": false,
"item_default": 1048576
+ },
+ {
+ "item_name": "tsig_key_ring",
+ "item_type": "list",
+ "item_optional": true,
+ "item_default": [],
+ "list_item_spec" :
+ {
+ "item_name": "tsig_key",
+ "item_type": "string",
+ "item_optional": true
+ }
}
],
"commands": [
diff --git a/src/lib/asiodns/io_fetch.cc b/src/lib/asiodns/io_fetch.cc
index e535381..4b2edf9 100644
--- a/src/lib/asiodns/io_fetch.cc
+++ b/src/lib/asiodns/io_fetch.cc
@@ -209,16 +209,6 @@ IOFetch::IOFetch(Protocol protocol, IOService& service,
msg->setHeaderFlag(Message::HEADERFLAG_CD,
query_message->getHeaderFlag(Message::HEADERFLAG_CD));
- ConstEDNSPtr edns(query_message->getEDNS());
- const bool dnssec_ok = edns && edns->getDNSSECAwareness();
- if (edns) {
- EDNSPtr edns_response(new EDNS());
- edns_response->setDNSSECAwareness(dnssec_ok);
- // TODO: We should make our own edns bufsize length configurable
- edns_response->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
- msg->setEDNS(edns_response);
- }
-
initIOFetch(msg, protocol, service,
**(query_message->beginQuestion()),
address, port, buff, cb, wait);
@@ -238,6 +228,9 @@ IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol, IOService& servic
query_msg->setRcode(Rcode::NOERROR());
query_msg->setHeaderFlag(Message::HEADERFLAG_RD);
query_msg->addQuestion(question);
+ EDNSPtr edns_query(new EDNS());
+ edns_query->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
+ query_msg->setEDNS(edns_query);
MessageRenderer renderer(*data_->msgbuf);
query_msg->toWire(renderer);
}
diff --git a/src/lib/asiodns/tests/Makefile.am b/src/lib/asiodns/tests/Makefile.am
index fd65d0b..f49d485 100644
--- a/src/lib/asiodns/tests/Makefile.am
+++ b/src/lib/asiodns/tests/Makefile.am
@@ -25,15 +25,15 @@ run_unittests_SOURCES += io_fetch_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
diff --git a/src/lib/asiodns/tests/io_fetch_unittest.cc b/src/lib/asiodns/tests/io_fetch_unittest.cc
index 2464b6d..52a51a1 100644
--- a/src/lib/asiodns/tests/io_fetch_unittest.cc
+++ b/src/lib/asiodns/tests/io_fetch_unittest.cc
@@ -130,6 +130,9 @@ public:
msg.setRcode(Rcode::NOERROR());
msg.setHeaderFlag(Message::HEADERFLAG_RD);
msg.addQuestion(question_);
+ EDNSPtr msg_edns(new EDNS());
+ msg_edns->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
+ msg.setEDNS(msg_edns);
MessageRenderer renderer(*msgbuf_);
msg.toWire(renderer);
MessageRenderer renderer2(*expected_buffer_);
diff --git a/src/lib/asiodns/tests/run_unittests.cc b/src/lib/asiodns/tests/run_unittests.cc
index c285f9e..df77368 100644
--- a/src/lib/asiodns/tests/run_unittests.cc
+++ b/src/lib/asiodns/tests/run_unittests.cc
@@ -13,16 +13,17 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
-#include <log/root_logger_name.h>
+#include <log/logger_manager.h>
#include <dns/tests/unittest_util.h>
int
main(int argc, char* argv[])
{
::testing::InitGoogleTest(&argc, argv); // Initialize Google test
- isc::log::setRootLoggerName("unittest"); // Set a root logger name
+ isc::log::LoggerManager::init("unittest"); // Set a root logger name
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR); // Add location of test data
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am
index c0d7af6..984cf07 100644
--- a/src/lib/asiolink/tests/Makefile.am
+++ b/src/lib/asiolink/tests/Makefile.am
@@ -34,13 +34,12 @@ run_unittests_SOURCES += udp_socket_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
diff --git a/src/lib/asiolink/tests/run_unittests.cc b/src/lib/asiolink/tests/run_unittests.cc
index 97bcb65..b07ce7e 100644
--- a/src/lib/asiolink/tests/run_unittests.cc
+++ b/src/lib/asiolink/tests/run_unittests.cc
@@ -13,15 +13,13 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
-
-#include <log/root_logger_name.h>
-#include <dns/tests/unittest_util.h>
+#include <util/unittests/run_all.h>
+#include <log/logger_manager.h>
int
main(int argc, char* argv[])
{
::testing::InitGoogleTest(&argc, argv); // Initialize Google test
- isc::log::setRootLoggerName("unittest"); // Set a root logger name
-
- return (RUN_ALL_TESTS());
+ isc::log::LoggerManager::init("unittest"); // Set a root logger name
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/bench/tests/Makefile.am b/src/lib/bench/tests/Makefile.am
index 4259b0e..3ebdf29 100644
--- a/src/lib/bench/tests/Makefile.am
+++ b/src/lib/bench/tests/Makefile.am
@@ -14,10 +14,10 @@ run_unittests_SOURCES += loadquery_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD = $(top_builddir)/src/lib/bench/libbench.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
-run_unittests_LDADD += $(top_builddir)/src/lib/bench/libbench.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(GTEST_LDADD)
endif
diff --git a/src/lib/bench/tests/run_unittests.cc b/src/lib/bench/tests/run_unittests.cc
index 85d4548..450f5dc 100644
--- a/src/lib/bench/tests/run_unittests.cc
+++ b/src/lib/bench/tests/run_unittests.cc
@@ -13,10 +13,11 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/cache/tests/Makefile.am b/src/lib/cache/tests/Makefile.am
index 68a8425..39215d9 100644
--- a/src/lib/cache/tests/Makefile.am
+++ b/src/lib/cache/tests/Makefile.am
@@ -32,20 +32,20 @@ TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
-run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
-run_unittests_SOURCES += rrset_entry_unittest.cc
-run_unittests_SOURCES += rrset_cache_unittest.cc
-run_unittests_SOURCES += message_cache_unittest.cc
-run_unittests_SOURCES += message_entry_unittest.cc
-run_unittests_SOURCES += local_zone_data_unittest.cc
-run_unittests_SOURCES += resolver_cache_unittest.cc
-run_unittests_SOURCES += negative_cache_unittest.cc
-run_unittests_SOURCES += cache_test_messagefromfile.h
-run_unittests_SOURCES += cache_test_sectioncount.h
+run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
+run_unittests_SOURCES += rrset_entry_unittest.cc
+run_unittests_SOURCES += rrset_cache_unittest.cc
+run_unittests_SOURCES += message_cache_unittest.cc
+run_unittests_SOURCES += message_entry_unittest.cc
+run_unittests_SOURCES += local_zone_data_unittest.cc
+run_unittests_SOURCES += resolver_cache_unittest.cc
+run_unittests_SOURCES += negative_cache_unittest.cc
+run_unittests_SOURCES += cache_test_messagefromfile.h
+run_unittests_SOURCES += cache_test_sectioncount.h
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
# NOTE: we may have to clean up this hack later (see the note in configure.ac)
if NEED_LIBBOOST_THREAD
@@ -55,14 +55,14 @@ endif
run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
noinst_PROGRAMS = $(TESTS)
-EXTRA_DIST = testdata/message_cname_referral.wire
+EXTRA_DIST = testdata/message_cname_referral.wire
EXTRA_DIST += testdata/message_example_com_soa.wire
EXTRA_DIST += testdata/message_fromWire1
EXTRA_DIST += testdata/message_fromWire2
diff --git a/src/lib/cache/tests/run_unittests.cc b/src/lib/cache/tests/run_unittests.cc
index 2c86581..b75fc06 100644
--- a/src/lib/cache/tests/run_unittests.cc
+++ b/src/lib/cache/tests/run_unittests.cc
@@ -15,6 +15,7 @@
#include <config.h>
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
@@ -24,5 +25,5 @@ main(int argc, char* argv[]) {
isc::UnitTestUtil::addDataPath(TEST_DATA_SRCDIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/cc/tests/Makefile.am b/src/lib/cc/tests/Makefile.am
index 71e6988..ebfd856 100644
--- a/src/lib/cc/tests/Makefile.am
+++ b/src/lib/cc/tests/Makefile.am
@@ -26,6 +26,7 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/cc/tests/run_unittests.cc b/src/lib/cc/tests/run_unittests.cc
index 0908071..eeef955 100644
--- a/src/lib/cc/tests/run_unittests.cc
+++ b/src/lib/cc/tests/run_unittests.cc
@@ -13,9 +13,10 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc
index 45710e3..857de63 100644
--- a/src/lib/config/ccsession.cc
+++ b/src/lib/config/ccsession.cc
@@ -35,6 +35,10 @@
#include <config/config_log.h>
#include <config/ccsession.h>
+#include <log/logger_support.h>
+#include <log/logger_specification.h>
+#include <log/logger_manager.h>
+
using namespace std;
using isc::data::Element;
@@ -151,6 +155,115 @@ parseCommand(ConstElementPtr& arg, ConstElementPtr command) {
}
}
+namespace {
+// Temporary workaround functions for missing functionality in
+// getValue() (main problem described in ticket #993)
+// This returns either the value set for the given relative id,
+// or its default value
+// (intentially defined here so this interface does not get
+// included in ConfigData as it is)
+ConstElementPtr getValueOrDefault(ConstElementPtr config_part,
+ const std::string& relative_id,
+ const ConfigData& config_data,
+ const std::string& full_id) {
+ if (config_part->contains(relative_id)) {
+ return config_part->get(relative_id);
+ } else {
+ return config_data.getDefaultValue(full_id);
+ }
+}
+
+// Reads a output_option subelement of a logger configuration,
+// and sets the values thereing to the given OutputOption struct,
+// or defaults values if they are not provided (from config_data).
+void
+readOutputOptionConf(isc::log::OutputOption& output_option,
+ ConstElementPtr output_option_el,
+ const ConfigData& config_data)
+{
+ ConstElementPtr destination_el = getValueOrDefault(output_option_el,
+ "destination", config_data,
+ "loggers/output_options/destination");
+ output_option.destination = isc::log::getDestination(destination_el->stringValue());
+ ConstElementPtr output_el = getValueOrDefault(output_option_el,
+ "output", config_data,
+ "loggers/output_options/output");
+ if (output_option.destination == isc::log::OutputOption::DEST_CONSOLE) {
+ output_option.stream = isc::log::getStream(output_el->stringValue());
+ } else if (output_option.destination == isc::log::OutputOption::DEST_FILE) {
+ output_option.filename = output_el->stringValue();
+ } else if (output_option.destination == isc::log::OutputOption::DEST_SYSLOG) {
+ output_option.facility = output_el->stringValue();
+ }
+ output_option.flush = getValueOrDefault(output_option_el,
+ "flush", config_data,
+ "loggers/output_options/flush")->boolValue();
+ output_option.maxsize = getValueOrDefault(output_option_el,
+ "maxsize", config_data,
+ "loggers/output_options/maxsize")->intValue();
+ output_option.maxver = getValueOrDefault(output_option_el,
+ "maxver", config_data,
+ "loggers/output_options/maxver")->intValue();
+}
+
+// Reads a full 'loggers' configuration, and adds the loggers therein
+// to the given vector, fills in blanks with defaults from config_data
+void
+readLoggersConf(std::vector<isc::log::LoggerSpecification>& specs,
+ ConstElementPtr logger,
+ const ConfigData& config_data)
+{
+ const std::string lname = logger->get("name")->stringValue();
+ ConstElementPtr severity_el = getValueOrDefault(logger,
+ "severity", config_data,
+ "loggers/severity");
+ isc::log::Severity severity = isc::log::getSeverity(
+ severity_el->stringValue());
+ int dbg_level = getValueOrDefault(logger, "debuglevel",
+ config_data,
+ "loggers/debuglevel")->intValue();
+ bool additive = getValueOrDefault(logger, "additive", config_data,
+ "loggers/additive")->boolValue();
+
+ isc::log::LoggerSpecification logger_spec(
+ lname, severity, dbg_level, additive
+ );
+
+ if (logger->contains("output_options")) {
+ BOOST_FOREACH(ConstElementPtr output_option_el,
+ logger->get("output_options")->listValue()) {
+ // create outputoptions
+ isc::log::OutputOption output_option;
+ readOutputOptionConf(output_option,
+ output_option_el,
+ config_data);
+ logger_spec.addOutputOption(output_option);
+ }
+ }
+
+ specs.push_back(logger_spec);
+}
+
+} // end anonymous namespace
+
+void
+my_logconfig_handler(const std::string&n, ConstElementPtr new_config, const ConfigData& config_data) {
+ config_data.getModuleSpec().validateConfig(new_config, true);
+
+ std::vector<isc::log::LoggerSpecification> specs;
+
+ if (new_config->contains("loggers")) {
+ BOOST_FOREACH(ConstElementPtr logger,
+ new_config->get("loggers")->listValue()) {
+ readLoggersConf(specs, logger, config_data);
+ }
+ }
+
+ isc::log::LoggerManager logger_manager;
+ logger_manager.process(specs.begin(), specs.end());
+}
+
+
ModuleSpec
ModuleCCSession::readModuleSpecification(const std::string& filename) {
std::ifstream file;
@@ -192,8 +305,11 @@ ModuleCCSession::ModuleCCSession(
isc::data::ConstElementPtr(*config_handler)(
isc::data::ConstElementPtr new_config),
isc::data::ConstElementPtr(*command_handler)(
- const std::string& command, isc::data::ConstElementPtr args)
+ const std::string& command, isc::data::ConstElementPtr args),
+ bool start_immediately,
+ bool handle_logging
) :
+ started_(false),
session_(session)
{
module_specification_ = readModuleSpecification(spec_file_name);
@@ -205,10 +321,8 @@ ModuleCCSession::ModuleCCSession(
session_.establish(NULL);
session_.subscribe(module_name_, "*");
- //session_.subscribe("Boss", "*");
- //session_.subscribe("statistics", "*");
- // send the data specification
+ // send the data specification
ConstElementPtr spec_msg = createCommand("module_spec",
module_specification_.getFullSpec());
unsigned int seq = session_.group_sendmsg(spec_msg, "ConfigManager");
@@ -237,8 +351,27 @@ ModuleCCSession::ModuleCCSession(
}
}
+ // Keep track of logging settings automatically
+ if (handle_logging) {
+ addRemoteConfig("Logging", my_logconfig_handler, false);
+ }
+
+ if (start_immediately) {
+ start();
+ }
+
+}
+
+void
+ModuleCCSession::start() {
+ if (started_) {
+ isc_throw(CCSessionError, "Module CC session already started");
+ }
+
// register callback for asynchronous read
session_.startRead(boost::bind(&ModuleCCSession::startCheck, this));
+
+ started_ = true;
}
/// Validates the new config values, if they are correct,
@@ -346,6 +479,11 @@ ModuleCCSession::checkCommand() {
}
} catch (const CCSessionError& re) {
LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG).arg(re.what());
+ } catch (const std::exception& stde) {
+ // No matter what unexpected error happens, we do not want
+ // to crash because of an incoming event, so we log the
+ // exception and continue to run
+ LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG_INTERNAL).arg(stde.what());
}
if (!isNull(answer)) {
session_.reply(routing, answer);
@@ -355,45 +493,55 @@ ModuleCCSession::checkCommand() {
return (0);
}
-std::string
-ModuleCCSession::addRemoteConfig(const std::string& spec_name,
- void (*handler)(const std::string& module,
- ConstElementPtr),
- bool spec_is_filename)
-{
- std::string module_name;
- ModuleSpec rmod_spec;
- if (spec_is_filename) {
- // It's a file name, so load it
- rmod_spec = readModuleSpecification(spec_name);
- module_name =
- rmod_spec.getFullSpec()->get("module_name")->stringValue();
+ModuleSpec
+ModuleCCSession::fetchRemoteSpec(const std::string& module, bool is_filename) {
+ if (is_filename) {
+ // It is a filename, simply load it.
+ return (readModuleSpecification(module));
} else {
// It's module name, request it from config manager
- ConstElementPtr cmd = Element::fromJSON("{ \"command\": ["
- "\"get_module_spec\","
- "{\"module_name\": \"" +
- module_name + "\"} ] }");
+
+ // Send the command
+ ConstElementPtr cmd(createCommand("get_module_spec",
+ Element::fromJSON("{\"module_name\": \"" + module +
+ "\"}")));
unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
ConstElementPtr env, answer;
session_.group_recvmsg(env, answer, false, seq);
int rcode;
ConstElementPtr spec_data = parseAnswer(rcode, answer);
if (rcode == 0 && spec_data) {
- rmod_spec = ModuleSpec(spec_data);
- module_name = spec_name;
- if (module_name != rmod_spec.getModuleName()) {
+ // received OK, construct the spec out of it
+ ModuleSpec spec = ModuleSpec(spec_data);
+ if (module != spec.getModuleName()) {
+ // It's a different module!
isc_throw(CCSessionError, "Module name mismatch");
}
+ return (spec);
} else {
- isc_throw(CCSessionError, "Error getting config for " + module_name + ": " + answer->str());
+ isc_throw(CCSessionError, "Error getting config for " +
+ module + ": " + answer->str());
}
}
- ConfigData rmod_config = ConfigData(rmod_spec);
+}
- // Get the current configuration values for that module
- ConstElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name + "\"} ] }");
- unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
+std::string
+ModuleCCSession::addRemoteConfig(const std::string& spec_name,
+ void (*handler)(const std::string& module,
+ ConstElementPtr,
+ const ConfigData&),
+ bool spec_is_filename)
+{
+ // First get the module name, specification and default config
+ const ModuleSpec rmod_spec(fetchRemoteSpec(spec_name, spec_is_filename));
+ const std::string module_name(rmod_spec.getModuleName());
+ ConfigData rmod_config(rmod_spec);
+
+ // Get the current configuration values from config manager
+ ConstElementPtr cmd(createCommand("get_config",
+ Element::fromJSON("{\"module_name\": \"" +
+ module_name + "\"}")));
+ const unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
ConstElementPtr env, answer;
session_.group_recvmsg(env, answer, false, seq);
@@ -401,6 +549,7 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_name,
ConstElementPtr new_config = parseAnswer(rcode, answer);
ElementPtr local_config;
if (rcode == 0 && new_config) {
+ // Merge the received config into existing local config
local_config = rmod_config.getLocalConfig();
isc::data::merge(local_config, new_config);
rmod_config.setLocalConfig(local_config);
@@ -412,8 +561,10 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_name,
remote_module_configs_[module_name] = rmod_config;
if (handler) {
remote_module_handlers_[module_name] = handler;
- handler(module_name, local_config);
+ handler(module_name, local_config, rmod_config);
}
+
+ // Make sure we get updates in future
session_.subscribe(module_name);
return (module_name);
}
@@ -458,7 +609,7 @@ ModuleCCSession::updateRemoteConfig(const std::string& module_name,
std::map<std::string, RemoteHandler>::iterator hit =
remote_module_handlers_.find(module_name);
if (hit != remote_module_handlers_.end()) {
- hit->second(module_name, new_config);
+ hit->second(module_name, new_config, it->second);
}
}
}
diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h
index c845b8f..53aab78 100644
--- a/src/lib/config/ccsession.h
+++ b/src/lib/config/ccsession.h
@@ -161,6 +161,7 @@ public:
* configuration of the local module needs to be updated.
* This must refer to a valid object of a concrete derived class of
* AbstractSession without establishing the session.
+ *
* Note: the design decision on who is responsible for establishing the
* session is in flux, and may change in near future.
*
@@ -171,6 +172,14 @@ public:
* @param command_handler A callback function pointer to be called when
* a control command from a remote agent needs to be performed on the
* local module.
+ * @param start_immediately If true (default), start listening to new commands
+ * and configuration changes asynchronously at the end of the constructor;
+ * if false, it will be delayed until the start() method is explicitly
+ * called. (This is a short term workaround for an initialization trouble.
+ * We'll need to develop a cleaner solution, and then remove this knob)
+ * @param handle_logging If true, the ModuleCCSession will automatically
+ * take care of logging configuration through the virtual Logging config
+ * module.
*/
ModuleCCSession(const std::string& spec_file_name,
isc::cc::AbstractSession& session,
@@ -178,9 +187,21 @@ public:
isc::data::ConstElementPtr new_config) = NULL,
isc::data::ConstElementPtr(*command_handler)(
const std::string& command,
- isc::data::ConstElementPtr args) = NULL
+ isc::data::ConstElementPtr args) = NULL,
+ bool start_immediately = true,
+ bool handle_logging = false
);
+ /// Start receiving new commands and configuration changes asynchronously.
+ ///
+ /// This method must be called only once, and only when the ModuleCCSession
+ /// was constructed with start_immediately being false. Otherwise
+ /// CCSessionError will be thrown.
+ ///
+ /// As noted in the constructor, this method should be considered a short
+ /// term workaround and will be removed in future.
+ void start();
+
/**
* Optional optimization for checkCommand loop; returns true
* if there are unhandled queued messages in the cc session.
@@ -240,12 +261,16 @@ public:
* for those changes. This function will subscribe to the relevant module
* channel.
*
+ * This method must be called before calling the \c start() method on the
+ * ModuleCCSession (it also implies the ModuleCCSession must have been
+ * constructed with start_immediately being false).
+ *
* \param spec_name This specifies the module to add. It is either a
* filename of the spec file to use or a name of module
* (in case it's a module name, the spec data is
* downloaded from the configuration manager, therefore
* the configuration manager must know it). If
- * spec_is_filenabe is true (the default), then a
+ * spec_is_filename is true (the default), then a
* filename is assumed, otherwise a module name.
* \param handler The handler function called whenever there's a change.
* Called once initally from this function. May be NULL
@@ -263,7 +288,8 @@ public:
std::string addRemoteConfig(const std::string& spec_name,
void (*handler)(const std::string& module_name,
isc::data::ConstElementPtr
- update) = NULL,
+ update,
+ const ConfigData& config_data) = NULL,
bool spec_is_filename = true);
/**
@@ -293,7 +319,8 @@ public:
private:
ModuleSpec readModuleSpecification(const std::string& filename);
void startCheck();
-
+
+ bool started_;
std::string module_name_;
isc::cc::AbstractSession& session_;
ModuleSpec module_specification_;
@@ -316,12 +343,15 @@ private:
isc::data::ConstElementPtr args);
typedef void (*RemoteHandler)(const std::string&,
- isc::data::ConstElementPtr);
+ isc::data::ConstElementPtr,
+ const ConfigData&);
std::map<std::string, ConfigData> remote_module_configs_;
std::map<std::string, RemoteHandler> remote_module_handlers_;
void updateRemoteConfig(const std::string& module_name,
isc::data::ConstElementPtr new_config);
+
+ ModuleSpec fetchRemoteSpec(const std::string& module, bool is_filename);
};
}
diff --git a/src/lib/config/config_data.cc b/src/lib/config/config_data.cc
index 1fa37c1..ebe51cc 100644
--- a/src/lib/config/config_data.cc
+++ b/src/lib/config/config_data.cc
@@ -21,6 +21,63 @@
using namespace isc::data;
+namespace {
+
+// Returns the '_spec' part of a list or map specification (recursively,
+// i.e. if it is a list of lists or maps, will return the spec of the
+// inner-most list or map).
+//
+// \param spec_part the list or map specification (part)
+// \return the value of spec_part's "list_item_spec" or "map_item_spec",
+// or the original spec_part, if it is not a MapElement or does
+// not contain "list_item_spec" or "map_item_spec"
+ConstElementPtr findListOrMapSubSpec(ConstElementPtr spec_part) {
+ while (spec_part->getType() == Element::map &&
+ (spec_part->contains("list_item_spec") ||
+ spec_part->contains("map_item_spec"))) {
+ if (spec_part->contains("list_item_spec")) {
+ spec_part = spec_part->get("list_item_spec");
+ } else {
+ spec_part = spec_part->get("map_item_spec");
+ }
+ }
+ return spec_part;
+}
+
+// Returns a specific Element in a given specification ListElement
+//
+// \exception DataNotFoundError if the given identifier does not
+// point to an existing element. Since we are dealing with the
+// specification here, and not the config data itself, this should
+// not happen, and is a code bug.
+//
+// \param spec_part ListElement to find the element in
+// \param id_part the name of the element to find (must match the value
+// "item_name" in the list item
+// \param id_full the full identifier id_part is a part of, this is
+// used to better report any errors
+ConstElementPtr findItemInSpecList(ConstElementPtr spec_part,
+ const std::string& id_part,
+ const std::string& id_full)
+{
+ bool found = false;
+ BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
+ if (list_el->getType() == Element::map &&
+ list_el->contains("item_name") &&
+ list_el->get("item_name")->stringValue() == id_part) {
+ spec_part = list_el;
+ found = true;
+ }
+ }
+ if (!found) {
+ isc_throw(isc::config::DataNotFoundError,
+ id_part + " in " + id_full + " not found");
+ }
+ return (spec_part);
+}
+
+} // anonymous namespace
+
namespace isc {
namespace config {
@@ -36,11 +93,10 @@ namespace config {
// validated and conforms to the specification.
static ConstElementPtr
find_spec_part(ConstElementPtr spec, const std::string& identifier) {
- //std::cout << "[XX] find_spec_part for " << identifier << std::endl;
if (!spec) {
isc_throw(DataNotFoundError, "Empty specification");
}
- //std::cout << "in: " << std::endl << spec << std::endl;
+
ConstElementPtr spec_part = spec;
if (identifier == "") {
isc_throw(DataNotFoundError, "Empty identifier");
@@ -49,59 +105,44 @@ find_spec_part(ConstElementPtr spec, const std::string& identifier) {
size_t sep = id.find('/');
while(sep != std::string::npos) {
std::string part = id.substr(0, sep);
- //std::cout << "[XX] id part: " << part << std::endl;
+
if (spec_part->getType() == Element::list) {
- bool found = false;
- BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
- if (list_el->getType() == Element::map &&
- list_el->contains("item_name") &&
- list_el->get("item_name")->stringValue() == part) {
- spec_part = list_el;
- found = true;
- }
- }
- if (!found) {
- isc_throw(DataNotFoundError, identifier);
- }
+ spec_part = findItemInSpecList(spec_part, part, identifier);
+ } else {
+ isc_throw(DataNotFoundError,
+ "Not a list of spec items: " + spec_part->str());
}
id = id.substr(sep + 1);
sep = id.find("/");
+
+ // As long as we are not in the 'final' element as specified
+ // by the identifier, we want to automatically traverse list
+ // and map specifications
+ if (id != "" && id != "/") {
+ spec_part = findListOrMapSubSpec(spec_part);
+ }
}
if (id != "" && id != "/") {
if (spec_part->getType() == Element::list) {
- bool found = false;
- BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
- if (list_el->getType() == Element::map &&
- list_el->contains("item_name") &&
- list_el->get("item_name")->stringValue() == id) {
- spec_part = list_el;
- found = true;
- }
- }
- if (!found) {
- isc_throw(DataNotFoundError, identifier);
- }
+ spec_part = findItemInSpecList(spec_part, id, identifier);
} else if (spec_part->getType() == Element::map) {
if (spec_part->contains("map_item_spec")) {
- bool found = false;
- BOOST_FOREACH(ConstElementPtr list_el,
- spec_part->get("map_item_spec")->listValue()) {
- if (list_el->getType() == Element::map &&
- list_el->contains("item_name") &&
- list_el->get("item_name")->stringValue() == id) {
- spec_part = list_el;
- found = true;
- }
- }
- if (!found) {
- isc_throw(DataNotFoundError, identifier);
- }
+ spec_part = findItemInSpecList(
+ spec_part->get("map_item_spec"),
+ id, identifier);
} else {
- isc_throw(DataNotFoundError, identifier);
+ // Either we already have the element we are looking
+ // for, or we are trying to reach something that does
+ // not exist (i.e. the code does not match the spec)
+ if (!spec_part->contains("item_name") ||
+ spec_part->get("item_name")->stringValue() != id) {
+ isc_throw(DataNotFoundError, "Element above " + id +
+ " in " + identifier +
+ " is not a map: " + spec_part->str());
+ }
}
}
}
- //std::cout << "[XX] found spec part: " << std::endl << spec_part << std::endl;
return (spec_part);
}
@@ -164,6 +205,17 @@ ConfigData::getValue(bool& is_default, const std::string& identifier) const {
return (value);
}
+ConstElementPtr
+ConfigData::getDefaultValue(const std::string& identifier) const {
+ ConstElementPtr spec_part =
+ find_spec_part(_module_spec.getConfigSpec(), identifier);
+ if (spec_part->contains("item_default")) {
+ return spec_part->get("item_default");
+ } else {
+ isc_throw(DataNotFoundError, "No default for " + identifier);
+ }
+}
+
/// Returns an ElementPtr pointing to a ListElement containing
/// StringElements with the names of the options at the given
/// identifier. If recurse is true, maps will be expanded as well
diff --git a/src/lib/config/config_data.h b/src/lib/config/config_data.h
index 29a5b5f..197d319 100644
--- a/src/lib/config/config_data.h
+++ b/src/lib/config/config_data.h
@@ -57,6 +57,16 @@ public:
/// value that is to be returned
isc::data::ConstElementPtr getValue(const std::string& identifier) const;
+ /// Returns the default value for the given identifier.
+ ///
+ /// \exception DataNotFoundError if the given identifier does not
+ /// exist, or if the given value has no specified default
+ ///
+ /// \param identifier The identifier pointing to the configuration
+ /// value for which the default is to be returned
+ /// \return ElementPtr containing the default value
+ isc::data::ConstElementPtr getDefaultValue(const std::string& identifier) const;
+
/// Returns the value currently set for the given identifier
/// If no value is set, the default value (as specified by the
/// .spec file) is returned. If there is no value and no default,
diff --git a/src/lib/config/configdef.mes b/src/lib/config/configdef.mes
index 4c3c991..be39073 100644
--- a/src/lib/config/configdef.mes
+++ b/src/lib/config/configdef.mes
@@ -48,3 +48,10 @@ channel. The message does not appear to be a valid command, and is
missing a required element or contains an unknown data format. This
most likely means that another BIND10 module is sending a bad message.
The message itself is ignored by this module.
+
+% CCSESSION_MSG_INTERNAL error handling CC session message: %1
+There was an internal problem handling an incoming message on the
+command and control channel. An unexpected exception was thrown. This
+most likely points to an internal inconsistency in the module code. The
+exception message is appended to the log error, and the module will
+continue to run, but will not send back an answer.
diff --git a/src/lib/config/tests/Makefile.am b/src/lib/config/tests/Makefile.am
index 0d2c29b..7153e09 100644
--- a/src/lib/config/tests/Makefile.am
+++ b/src/lib/config/tests/Makefile.am
@@ -22,11 +22,12 @@ run_unittests_SOURCES = ccsession_unittests.cc module_spec_unittests.cc config_d
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += libfake_session.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-run_unittests_LDADD += libfake_session.la
-run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc
index 3564d4b..e5fe049 100644
--- a/src/lib/config/tests/ccsession_unittests.cc
+++ b/src/lib/config/tests/ccsession_unittests.cc
@@ -160,6 +160,10 @@ TEST_F(CCSessionTest, session1) {
EXPECT_EQ("ConfigManager", group);
EXPECT_EQ("*", to);
EXPECT_EQ(0, session.getMsgQueue()->size());
+
+ // without explicit argument, the session should not automatically
+ // subscribe to logging config
+ EXPECT_FALSE(session.haveSubscription("Logging", "*"));
}
TEST_F(CCSessionTest, session2) {
@@ -351,7 +355,9 @@ int remote_item1(0);
ConstElementPtr remote_config;
ModuleCCSession *remote_mccs(NULL);
-void remoteHandler(const std::string& module_name, ConstElementPtr config) {
+void remoteHandler(const std::string& module_name,
+ ConstElementPtr config,
+ const ConfigData&) {
remote_module_name = module_name;
remote_item1 = remote_mccs->getRemoteConfigValue("Spec2", "item1")->
intValue();
@@ -362,7 +368,7 @@ TEST_F(CCSessionTest, remoteConfig) {
std::string module_name;
int item1;
- ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL);
+ ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL, false);
EXPECT_TRUE(session.haveSubscription("Spec1", "*"));
// first simply connect, with no config values, and see we get
@@ -415,7 +421,16 @@ TEST_F(CCSessionTest, remoteConfig) {
EXPECT_NO_THROW(module_name = mccs.addRemoteConfig("Spec2", NULL,
false));
+ const size_t qsize(session.getMsgQueue()->size());
+ EXPECT_TRUE(session.getMsgQueue()->get(qsize - 2)->equals(*el(
+ "[ \"ConfigManager\", \"*\", { \"command\": ["
+ "\"get_module_spec\", { \"module_name\": \"Spec2\" } ] } ]")));
+ EXPECT_TRUE(session.getMsgQueue()->get(qsize - 1)->equals(*el(
+ "[ \"ConfigManager\", \"*\", { \"command\": [ \"get_config\","
+ "{ \"module_name\": \"Spec2\" } ] } ]")));
EXPECT_EQ("Spec2", module_name);
+ // Since we returned an empty local config above, the default value
+ // for "item1", which is 1, should be used.
EXPECT_NO_THROW(item1 =
mccs.getRemoteConfigValue(module_name,
"item1")->intValue());
@@ -425,6 +440,18 @@ TEST_F(CCSessionTest, remoteConfig) {
}
{
+ SCOPED_TRACE("With bad module name");
+ // It is almost the same as above, but we supply wrong module name.
+ // It should fail.
+ // Try adding it with downloading the spec from config manager
+ ModuleSpec spec(moduleSpecFromFile(ccspecfile("spec2.spec")));
+ session.getMessages()->add(createAnswer(0, spec.getFullSpec()));
+
+ EXPECT_THROW(module_name = mccs.addRemoteConfig("Spec1", NULL, false),
+ CCSessionError);
+ }
+
+ {
// Try adding it with a handler.
// Pass non-default value to see the handler is called after
// downloading the configuration, not too soon.
@@ -496,7 +523,8 @@ TEST_F(CCSessionTest, ignoreRemoteConfigCommands) {
session.getMessages()->add(createAnswer(0, el("{ }")));
EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
- ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler, my_command_handler);
+ ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler,
+ my_command_handler, false);
EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
EXPECT_EQ(2, session.getMsgQueue()->size());
@@ -546,4 +574,62 @@ TEST_F(CCSessionTest, initializationFail) {
EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
}
+// Test it throws when we try to start it twice (once from the constructor)
+TEST_F(CCSessionTest, doubleStartImplicit) {
+ ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL);
+ EXPECT_THROW(mccs.start(), CCSessionError);
+}
+
+// The same, but both starts are explicit
+TEST_F(CCSessionTest, doubleStartExplicit) {
+ ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL,
+ false);
+ mccs.start();
+ EXPECT_THROW(mccs.start(), CCSessionError);
+}
+
+// Test we can request synchronous receive before we start the session,
+// and check there's the mechanism if we do it after
+TEST_F(CCSessionTest, delayedStart) {
+ ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL, false);
+ session.getMessages()->add(createAnswer());
+ ConstElementPtr env, answer;
+ EXPECT_NO_THROW(session.group_recvmsg(env, answer, false, 3));
+ mccs.start();
+ session.getMessages()->add(createAnswer());
+ EXPECT_THROW(session.group_recvmsg(env, answer, false, 3),
+ FakeSession::DoubleRead);
+}
+
+TEST_F(CCSessionTest, loggingStart) {
+ // provide the logging module spec
+ ConstElementPtr log_spec = moduleSpecFromFile(LOG_SPEC_FILE).getFullSpec();
+ session.getMessages()->add(createAnswer(0, log_spec));
+ // just give an empty config
+ session.getMessages()->add(createAnswer(0, el("{}")));
+ ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL,
+ true, true);
+ EXPECT_TRUE(session.haveSubscription("Logging", "*"));
+}
+
+TEST_F(CCSessionTest, loggingStartBadSpec) {
+ // provide the logging module spec
+ session.getMessages()->add(createAnswer(0, el("{}")));
+ // just give an empty config
+ session.getMessages()->add(createAnswer(0, el("{}")));
+ EXPECT_THROW(new ModuleCCSession(ccspecfile("spec2.spec"), session,
+ NULL, NULL, true, true), ModuleSpecError);
+ EXPECT_FALSE(session.haveSubscription("Logging", "*"));
+}
+
+// Similar to the above, but more implicitly by calling addRemoteConfig().
+// We should construct ModuleCCSession with start_immediately being false
+// if we need to call addRemoteConfig().
+// The correct cases are covered in remoteConfig test.
+TEST_F(CCSessionTest, doubleStartWithAddRemoteConfig) {
+ ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL);
+ session.getMessages()->add(createAnswer(0, el("{}")));
+ EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")),
+ FakeSession::DoubleRead);
+}
}
diff --git a/src/lib/config/tests/config_data_unittests.cc b/src/lib/config/tests/config_data_unittests.cc
index 974812d..26a3fc6 100644
--- a/src/lib/config/tests/config_data_unittests.cc
+++ b/src/lib/config/tests/config_data_unittests.cc
@@ -64,21 +64,35 @@ TEST(ConfigData, getValue) {
EXPECT_EQ("{ }", cd.getValue(is_default, "value6/")->str());
EXPECT_TRUE(is_default);
EXPECT_EQ("[ ]", cd.getValue("value8")->str());
+ EXPECT_EQ("[ ]", cd.getDefaultValue("value8")->str());
+ EXPECT_EQ("empty", cd.getValue("value8/a")->stringValue());
EXPECT_THROW(cd.getValue("")->str(), DataNotFoundError);
EXPECT_THROW(cd.getValue("/")->str(), DataNotFoundError);
EXPECT_THROW(cd.getValue("no_such_item")->str(), DataNotFoundError);
EXPECT_THROW(cd.getValue("value6/a")->str(), DataNotFoundError);
EXPECT_THROW(cd.getValue("value6/no_such_item")->str(), DataNotFoundError);
- EXPECT_THROW(cd.getValue("value8/a")->str(), DataNotFoundError);
- EXPECT_THROW(cd.getValue("value8/a")->str(), DataNotFoundError);
- EXPECT_THROW(cd.getValue("value8/a")->str(), DataNotFoundError);
+ EXPECT_THROW(cd.getValue("value8/b")->str(), DataNotFoundError);
ModuleSpec spec1 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec1.spec");
ConfigData cd1 = ConfigData(spec1);
EXPECT_THROW(cd1.getValue("anything")->str(), DataNotFoundError);
}
+TEST(ConfigData, getDefaultValue) {
+ ModuleSpec spec31 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec31.spec");
+ ConfigData cd = ConfigData(spec31);
+ EXPECT_EQ("[ ]", cd.getDefaultValue("first_list_items")->str());
+ EXPECT_EQ("\"foo\"", cd.getDefaultValue("first_list_items/foo")->str());
+ EXPECT_EQ("{ }", cd.getDefaultValue("first_list_items/second_list_items/map_element")->str());
+ EXPECT_EQ("[ ]", cd.getDefaultValue("first_list_items/second_list_items/map_element/list1")->str());
+ EXPECT_EQ("1", cd.getDefaultValue("first_list_items/second_list_items/map_element/list1/number")->str());
+
+ EXPECT_THROW(cd.getDefaultValue("doesnotexist")->str(), DataNotFoundError);
+ EXPECT_THROW(cd.getDefaultValue("first_list_items/second_list_items/map_element/list1/doesnotexist")->str(), DataNotFoundError);
+}
+
+
TEST(ConfigData, setLocalConfig) {
ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
ConfigData cd = ConfigData(spec2);
diff --git a/src/lib/config/tests/data_def_unittests_config.h.in b/src/lib/config/tests/data_def_unittests_config.h.in
index 80e9cfa..f9662f0 100644
--- a/src/lib/config/tests/data_def_unittests_config.h.in
+++ b/src/lib/config/tests/data_def_unittests_config.h.in
@@ -13,3 +13,4 @@
// PERFORMANCE OF THIS SOFTWARE.
#define TEST_DATA_PATH "@abs_srcdir@/testdata"
+#define LOG_SPEC_FILE "@abs_top_srcdir@/src/bin/cfgmgr/plugins/logging.spec"
diff --git a/src/lib/config/tests/fake_session.cc b/src/lib/config/tests/fake_session.cc
index 5f79d48..2b216e7 100644
--- a/src/lib/config/tests/fake_session.cc
+++ b/src/lib/config/tests/fake_session.cc
@@ -71,7 +71,8 @@ FakeSession::FakeSession(isc::data::ElementPtr initial_messages,
isc::data::ElementPtr msg_queue) :
messages_(initial_messages),
subscriptions_(subscriptions),
- msg_queue_(msg_queue)
+ msg_queue_(msg_queue),
+ started_(false)
{
}
@@ -84,6 +85,7 @@ FakeSession::disconnect() {
void
FakeSession::startRead(boost::function<void()>) {
+ started_ = true;
}
void
@@ -91,7 +93,13 @@ FakeSession::establish(const char*) {
}
bool
-FakeSession::recvmsg(ConstElementPtr& msg, bool, int) {
+FakeSession::recvmsg(ConstElementPtr& msg, bool nonblock, int) {
+ if (started_ && !nonblock) {
+ // This would schedule another read for length, leading to
+ // corputed data
+ isc_throw(DoubleRead, "Second read scheduled from recvmsg");
+ }
+
//cout << "[XX] client asks for message " << endl;
if (messages_ &&
messages_->getType() == Element::list &&
@@ -105,7 +113,15 @@ FakeSession::recvmsg(ConstElementPtr& msg, bool, int) {
}
bool
-FakeSession::recvmsg(ConstElementPtr& env, ConstElementPtr& msg, bool, int) {
+FakeSession::recvmsg(ConstElementPtr& env, ConstElementPtr& msg, bool nonblock,
+ int)
+{
+ if (started_ && !nonblock) {
+ // This would schedule another read for length, leading to
+ // corputed data
+ isc_throw(DoubleRead, "Second read scheduled from recvmsg");
+ }
+
//cout << "[XX] client asks for message and env" << endl;
env = ElementPtr();
if (messages_ &&
diff --git a/src/lib/config/tests/fake_session.h b/src/lib/config/tests/fake_session.h
index ac8e291..85e47d5 100644
--- a/src/lib/config/tests/fake_session.h
+++ b/src/lib/config/tests/fake_session.h
@@ -42,6 +42,14 @@ public:
isc::data::ElementPtr msg_queue);
virtual ~FakeSession();
+ // This is thrown if two reads for length at once are scheduled at once.
+ // Such thing does bad things currently (see discussion in ticket #931).
+ class DoubleRead : public Exception {
+ public:
+ DoubleRead(const char* file, size_t line, const char* what) :
+ Exception(file, line, what) {}
+ };
+
virtual void startRead(boost::function<void()> read_callback);
virtual void establish(const char* socket_file = NULL);
@@ -89,6 +97,7 @@ private:
const isc::data::ElementPtr messages_;
isc::data::ElementPtr subscriptions_;
isc::data::ElementPtr msg_queue_;
+ bool started_;
};
} // namespace cc
} // namespace isc
diff --git a/src/lib/config/tests/run_unittests.cc b/src/lib/config/tests/run_unittests.cc
index fab90f5..19d2be1 100644
--- a/src/lib/config/tests/run_unittests.cc
+++ b/src/lib/config/tests/run_unittests.cc
@@ -13,16 +13,12 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <log/logger_support.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
-
- // TODO: UNCOMMENT ON MERGE
- // (this is the call we want in master, but branch point does not
- // have this yet)
- //isc::log::initLogger();
-
- return (RUN_ALL_TESTS());
+ isc::log::initLogger();
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/config/tests/testdata/Makefile.am b/src/lib/config/tests/testdata/Makefile.am
index 94c087d..57d1ed3 100644
--- a/src/lib/config/tests/testdata/Makefile.am
+++ b/src/lib/config/tests/testdata/Makefile.am
@@ -51,3 +51,5 @@ EXTRA_DIST += spec26.spec
EXTRA_DIST += spec27.spec
EXTRA_DIST += spec28.spec
EXTRA_DIST += spec29.spec
+EXTRA_DIST += spec30.spec
+EXTRA_DIST += spec31.spec
diff --git a/src/lib/config/tests/testdata/spec30.spec b/src/lib/config/tests/testdata/spec30.spec
new file mode 100644
index 0000000..a9e00ad
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec30.spec
@@ -0,0 +1,45 @@
+{
+ "module_spec": {
+ "module_name": "lists",
+ "module_description": "Logging options",
+ "config_data": [
+ {
+ "item_name": "first_list_items",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "first_list_item",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "foo",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "foo"
+ },
+ { "item_name": "second_list_items",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "second_list_item",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "final_element",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "hello"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/src/lib/config/tests/testdata/spec31.spec b/src/lib/config/tests/testdata/spec31.spec
new file mode 100644
index 0000000..9eebfd1
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec31.spec
@@ -0,0 +1,63 @@
+{
+ "module_spec": {
+ "module_name": "lists",
+ "module_description": "Logging options",
+ "config_data": [
+ {
+ "item_name": "first_list_items",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "first_list_item",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "foo",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "foo"
+ },
+ { "item_name": "second_list_items",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "second_list_item",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "map_element",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "list1",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec":
+ { "item_name": "list2",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec":
+ { "item_name": "number",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 1
+ }
+ }
+ }]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/src/lib/cryptolink/crypto_hmac.cc b/src/lib/cryptolink/crypto_hmac.cc
index 14b43b3..277b036 100644
--- a/src/lib/cryptolink/crypto_hmac.cc
+++ b/src/lib/cryptolink/crypto_hmac.cc
@@ -183,9 +183,9 @@ public:
try {
Botan::SecureVector<Botan::byte> our_mac = hmac_->final();
if (len < getOutputLength()) {
- // Currently we don't support truncated signature. To avoid
- // validating too short signature accidently, we enforce the
- // standard signature size for the moment.
+ // Currently we don't support truncated signature in TSIG (see
+ // #920). To avoid validating too short signature accidently,
+ // we enforce the standard signature size for the moment.
// Once we support truncation correctly, this if-clause should
// (and the capitalized comment above) be removed.
return (false);
@@ -246,7 +246,7 @@ HMAC::verify(const void* sig, const size_t len) {
}
void
-signHMAC(const void* data, size_t data_len, const void* secret,
+signHMAC(const void* data, const size_t data_len, const void* secret,
size_t secret_len, const HashAlgorithm hash_algorithm,
isc::util::OutputBuffer& result, size_t len)
{
diff --git a/src/lib/cryptolink/tests/Makefile.am b/src/lib/cryptolink/tests/Makefile.am
index c8b5e26..fbdd13f 100644
--- a/src/lib/cryptolink/tests/Makefile.am
+++ b/src/lib/cryptolink/tests/Makefile.am
@@ -16,10 +16,11 @@ TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += crypto_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDFLAGS = ${BOTAN_LDFLAGS} $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libcryptolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libcryptolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/cryptolink/tests/crypto_unittests.cc b/src/lib/cryptolink/tests/crypto_unittests.cc
index 339eb1b..4abeb87 100644
--- a/src/lib/cryptolink/tests/crypto_unittests.cc
+++ b/src/lib/cryptolink/tests/crypto_unittests.cc
@@ -13,8 +13,16 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
+
+#include <string>
+#include <vector>
+
+#include <boost/lexical_cast.hpp>
+
#include <gtest/gtest.h>
+#include <util/encode/hex.h>
+
#include <cryptolink/cryptolink.h>
#include <cryptolink/crypto_hmac.h>
@@ -23,7 +31,9 @@
#include <boost/shared_ptr.hpp>
+using namespace boost;
using namespace isc::util;
+using namespace isc::util::encode;
using namespace isc::cryptolink;
namespace {
@@ -340,314 +350,158 @@ TEST(CryptoLinkTest, DISABLED_HMAC_SHA1_RFC2202_SIGN_TRUNCATED) {
//
// Test values taken from RFC 4231
//
-//
// Test data from RFC4231, including secret key
// and source data, they are common for sha224/256/384/512
-// so put them together in seperate space
-namespace {
- static const uint8_t secret1[] = {
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
- };
-
- static const uint8_t secret2[] = "Jefe";
- static const uint8_t secret3[] = {
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa
- };
- static const uint8_t secret4[] = {
+// so put them together within the separate function.
+void
+doRFC4231Tests(HashAlgorithm hash_algorithm,
+ const std::vector<std::vector<uint8_t> >& hmac_list)
+{
+ std::vector<std::string> data_list;
+ std::vector<std::string> secret_list;
+
+ data_list.push_back("Hi There");
+ data_list.push_back("what do ya want for nothing?");
+ data_list.push_back(std::string(50, 0xdd));
+ data_list.push_back(std::string(50, 0xcd));
+ data_list.push_back("Test With Truncation");
+ data_list.push_back("Test Using Larger Than Block-Size Key - "
+ "Hash Key First");
+ data_list.push_back("This is a test using a larger than block-size "
+ "key and a larger than block-size data. The key "
+ "needs to be hashed before being used by the HMAC "
+ "algorithm.");
+
+ secret_list.push_back(std::string(20, 0x0b));
+ secret_list.push_back("Jefe");
+ secret_list.push_back(std::string(20, 0xaa));
+ const uint8_t secret_array[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19
};
- static const uint8_t secret5[] = {
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c
- };
-
- static uint8_t secret6[131];
- static uint8_t secret7[131];
-
- static const std::string data1("Hi There");
- static const std::string data2("what do ya want for nothing?");
- static const std::string data3(std::string(50, 0xdd));
- static const std::string data4(std::string(50, 0xcd));
- static const std::string data5("Test With Truncation");
- static const std::string data6("Test Using Larger Than Block-Size Key - Hash Key First");
- static const std::string data7("This is a test using a larger than block-size key and a"
- " larger than block-size data. The key needs to be hashe"
- "d before being used by the HMAC algorithm.");
-#define SECRECT(n) secret##n
-#define DATA(n) data##n
-#define HMAC_EXPECTED(n) hmac_expected##n
-
-#define RUN_NTH_TEST_CASE_FOR_ALG(index, hash_algorithm) do {\
- doHMACTest(DATA(index), \
- SECRECT(index), sizeof(SECRECT(index)), \
- (hash_algorithm), HMAC_EXPECTED(index), \
- sizeof(HMAC_EXPECTED(index)));\
- }while(0)
-
-#define RUN_TEST_CASES_FOR_ALG(alg) do {\
- memcpy(secret6, std::string(131, 0xaa).c_str(), 131); \
- memcpy(secret7, std::string(131, 0xaa).c_str(), 131); \
- RUN_NTH_TEST_CASE_FOR_ALG(1, alg); \
- RUN_NTH_TEST_CASE_FOR_ALG(2, alg); \
- RUN_NTH_TEST_CASE_FOR_ALG(3, alg); \
- RUN_NTH_TEST_CASE_FOR_ALG(4, alg); \
- RUN_NTH_TEST_CASE_FOR_ALG(5, alg); \
- RUN_NTH_TEST_CASE_FOR_ALG(6, alg); \
- RUN_NTH_TEST_CASE_FOR_ALG(7, alg); \
- }while(0)
-
-
-};
+ secret_list.push_back(std::string(secret_array,
+ secret_array + sizeof(secret_array)));
+ secret_list.push_back(std::string(20, 0x0c));
+ secret_list.push_back(std::string(131, 0xaa));
+ secret_list.push_back(std::string(131, 0xaa));
+
+ // Make sure we provide a consistent size of test data
+ ASSERT_EQ(secret_list.size(), data_list.size());
+ ASSERT_EQ(secret_list.size(), hmac_list.size());
+
+ for (int i = 0; i < data_list.size(); ++i) {
+ SCOPED_TRACE("RFC4231 HMAC test for algorithm ID: " +
+ lexical_cast<std::string>(hash_algorithm) +
+ ", data ID: " + lexical_cast<std::string>(i));
+ // Until #920 is resolved we have to skip truncation cases.
+ if (data_list[i] == "Test With Truncation") {
+ continue;
+ }
+ doHMACTest(data_list[i], secret_list[i].c_str(), secret_list[i].size(),
+ hash_algorithm, &hmac_list[i][0], hmac_list[i].size());
+ }
+}
TEST(CryptoLinkTest, HMAC_SHA256_RFC4231_SIGN) {
- const uint8_t hmac_expected1[] = { 0xb0, 0x34, 0x4c, 0x61, 0xd8,
- 0xdb, 0x38, 0x53, 0x5c, 0xa8,
- 0xaf, 0xce, 0xaf, 0x0b, 0xf1,
- 0x2b, 0x88, 0x1d, 0xc2, 0x00,
- 0xc9, 0x83, 0x3d, 0xa7, 0x26,
- 0xe9, 0x37, 0x6c, 0x2e, 0x32,
- 0xcf, 0xf7 };
- const uint8_t hmac_expected2[] = { 0x5b, 0xdc, 0xc1, 0x46, 0xbf,
- 0x60, 0x75, 0x4e, 0x6a, 0x04,
- 0x24, 0x26, 0x08, 0x95, 0x75,
- 0xc7, 0x5a, 0x00, 0x3f, 0x08,
- 0x9d, 0x27, 0x39, 0x83, 0x9d,
- 0xec, 0x58, 0xb9, 0x64, 0xec,
- 0x38, 0x43 };
- const uint8_t hmac_expected3[] = { 0x77, 0x3e, 0xa9, 0x1e, 0x36,
- 0x80, 0x0e, 0x46, 0x85, 0x4d,
- 0xb8, 0xeb, 0xd0, 0x91, 0x81,
- 0xa7, 0x29, 0x59, 0x09, 0x8b,
- 0x3e, 0xf8, 0xc1, 0x22, 0xd9,
- 0x63, 0x55, 0x14, 0xce, 0xd5,
- 0x65, 0xfe };
- const uint8_t hmac_expected4[] = { 0x82, 0x55, 0x8a, 0x38, 0x9a,
- 0x44, 0x3c, 0x0e, 0xa4, 0xcc,
- 0x81, 0x98, 0x99, 0xf2, 0x08,
- 0x3a, 0x85, 0xf0, 0xfa, 0xa3,
- 0xe5, 0x78, 0xf8, 0x07, 0x7a,
- 0x2e, 0x3f, 0xf4, 0x67, 0x29,
- 0x66, 0x5b };
-// const uint8_t hmac_expected5[] = { 0xa3, 0xb6, 0x16, 0x74, 0x73,
-// 0x10, 0x0e, 0xe0, 0x6e, 0x0c,
-// 0x79, 0x6c, 0x29, 0x55, 0x55,
-// 0x2b };
- const uint8_t hmac_expected6[] = { 0x60, 0xe4, 0x31, 0x59, 0x1e,
- 0xe0, 0xb6, 0x7f, 0x0d, 0x8a,
- 0x26, 0xaa, 0xcb, 0xf5, 0xb7,
- 0x7f, 0x8e, 0x0b, 0xc6, 0x21,
- 0x37, 0x28, 0xc5, 0x14, 0x05,
- 0x46, 0x04, 0x0f, 0x0e, 0xe3,
- 0x7f, 0x54 };
- const uint8_t hmac_expected7[] = { 0x9b, 0x09, 0xff, 0xa7, 0x1b,
- 0x94, 0x2f, 0xcb, 0x27, 0x63,
- 0x5f, 0xbc, 0xd5, 0xb0, 0xe9,
- 0x44, 0xbf, 0xdc, 0x63, 0x64,
- 0x4f, 0x07, 0x13, 0x93, 0x8a,
- 0x7f, 0x51, 0x53, 0x5c, 0x3a,
- 0x35, 0xe2 };
-
- memcpy(secret6, std::string(131, 0xaa).c_str(), 131);
- memcpy(secret7, std::string(131, 0xaa).c_str(), 131);
- RUN_NTH_TEST_CASE_FOR_ALG(1, SHA256);
- RUN_NTH_TEST_CASE_FOR_ALG(2, SHA256);
- RUN_NTH_TEST_CASE_FOR_ALG(3, SHA256);
- RUN_NTH_TEST_CASE_FOR_ALG(4, SHA256);
- RUN_NTH_TEST_CASE_FOR_ALG(6, SHA256);
- RUN_NTH_TEST_CASE_FOR_ALG(7, SHA256);
-
+ std::vector<std::vector<uint8_t> > hmac_expected_list(7);
+
+ int i = 0;
+ decodeHex(
+ "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7",
+ hmac_expected_list[i++]);
+ decodeHex(
+ "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843",
+ hmac_expected_list[i++]);
+ decodeHex(
+ "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe",
+ hmac_expected_list[i++]);
+ decodeHex(
+ "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b",
+ hmac_expected_list[i++]);
+ decodeHex("a3b6167473100ee06e0c796c2955552b", hmac_expected_list[i++]);
+ decodeHex(
+ "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54",
+ hmac_expected_list[i++]);
+ decodeHex(
+ "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2",
+ hmac_expected_list[i++]);
+
+ doRFC4231Tests(SHA256, hmac_expected_list);
}
-
//
// Test values taken from RFC 4231, test optional algorithm 224,384,512
//
-TEST(CryptoLinkTest, DISABLED_HMAC_SHA224_RFC4231_SIGN) {
- const uint8_t hmac_expected1[] = {
- 0x89,0x6f,0xb1,0x12,0x8a,0xbb,0xdf,0x19,0x68,0x32,0x10,0x7c,
- 0xd4,0x9d,0xf3,0x3f,0x47,0xb4,0xb1,0x16,0x99,0x12,0xba,0x4f,
- 0x53,0x68,0x4b,0x22
- };
- const uint8_t hmac_expected2[] = {
- 0xa3,0x0e,0x01,0x09,0x8b,0xc6,0xdb,0xbf,0x45,0x69,0x0f,0x3a,
- 0x7e,0x9e,0x6d,0x0f,0x8b,0xbe,0xa2,0xa3,0x9e,0x61,0x48,0x00,
- 0x8f,0xd0,0x5e,0x44
- };
-
- const uint8_t hmac_expected3[] = {
- 0x7f,0xb3,0xcb,0x35,0x88,0xc6,0xc1,0xf6,0xff,0xa9,0x69,0x4d,
- 0x7d,0x6a,0xd2,0x64,0x93,0x65,0xb0,0xc1,0xf6,0x5d,0x69,0xd1,
- 0xec,0x83,0x33,0xea
- };
-
- const uint8_t hmac_expected4[] = {
- 0x6c,0x11,0x50,0x68,0x74,0x01,0x3c,0xac,0x6a,0x2a,0xbc,0x1b,
- 0xb3,0x82,0x62,0x7c,0xec,0x6a,0x90,0xd8,0x6e,0xfc,0x01,0x2d,
- 0xe7,0xaf,0xec,0x5a
- };
-
-// const uint8_t hmac_expected5[] = {
-// 0x0e,0x2a,0xea,0x68,0xa9,0x0c,0x8d,0x37,0xc9,0x88,0xbc,0xdb,0x9f,
-// 0xca,0x6f,0xa8
-// };
-
- const uint8_t hmac_expected6[] = {
- 0x95,0xe9,0xa0,0xdb,0x96,0x20,0x95,0xad,0xae,0xbe,0x9b,0x2d,0x6f,
- 0x0d,0xbc,0xe2,0xd4,0x99,0xf1,0x12,0xf2,0xd2,0xb7,0x27,0x3f,0xa6,
- 0x87,0x0e
- };
-
- const uint8_t hmac_expected7[] = {
- 0x3a,0x85,0x41,0x66,0xac,0x5d,0x9f,0x02,0x3f,0x54,0xd5,0x17,0xd0,
- 0xb3,0x9d,0xbd,0x94,0x67,0x70,0xdb,0x9c,0x2b,0x95,0xc9,0xf6,0xf5,
- 0x65,0xd1
- };
-
- memcpy(secret6, std::string(131, 0xaa).c_str(), 131);
- memcpy(secret7, std::string(131, 0xaa).c_str(), 131);
- RUN_NTH_TEST_CASE_FOR_ALG(1, SHA224);
- RUN_NTH_TEST_CASE_FOR_ALG(2, SHA224);
- RUN_NTH_TEST_CASE_FOR_ALG(3, SHA224);
- RUN_NTH_TEST_CASE_FOR_ALG(4, SHA224);
- RUN_NTH_TEST_CASE_FOR_ALG(6, SHA224);
- RUN_NTH_TEST_CASE_FOR_ALG(7, SHA224);
+TEST(CryptoLinkTest, HMAC_SHA224_RFC4231_SIGN) {
+ std::vector<std::vector<uint8_t> > hmac_expected_list(7);
+
+ int i = 0;
+ decodeHex("896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22",
+ hmac_expected_list[i++]);
+ decodeHex("a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44",
+ hmac_expected_list[i++]);
+ decodeHex("7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea",
+ hmac_expected_list[i++]);
+ decodeHex("6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a",
+ hmac_expected_list[i++]);
+ decodeHex("0e2aea68a90c8d37c988bcdb9fca6fa8", hmac_expected_list[i++]);
+ decodeHex("95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e",
+ hmac_expected_list[i++]);
+ decodeHex("3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1",
+ hmac_expected_list[i++]);
+
+ doRFC4231Tests(SHA224, hmac_expected_list);
}
-
TEST(CryptoLinkTest, HMAC_SHA384_RFC4231_SIGN) {
- const uint8_t hmac_expected1[] = {
- 0xaf,0xd0,0x39,0x44,0xd8,0x48,0x95,0x62,0x6b,0x08,0x25,0xf4,
- 0xab,0x46,0x90,0x7f,0x15,0xf9,0xda,0xdb,0xe4,0x10,0x1e,0xc6,
- 0x82,0xaa,0x03,0x4c,0x7c,0xeb,0xc5,0x9c,0xfa,0xea,0x9e,0xa9,
- 0x07,0x6e,0xde,0x7f,0x4a,0xf1,0x52,0xe8,0xb2,0xfa,0x9c,0xb6
- };
-
- const uint8_t hmac_expected2[] = {
- 0xaf,0x45,0xd2,0xe3,0x76,0x48,0x40,0x31,0x61,0x7f,0x78,0xd2,
- 0xb5,0x8a,0x6b,0x1b,0x9c,0x7e,0xf4,0x64,0xf5,0xa0,0x1b,0x47,
- 0xe4,0x2e,0xc3,0x73,0x63,0x22,0x44,0x5e,0x8e,0x22,0x40,0xca,
- 0x5e,0x69,0xe2,0xc7,0x8b,0x32,0x39,0xec,0xfa,0xb2,0x16,0x49
- };
-
- const uint8_t hmac_expected3[] = {
- 0x88,0x06,0x26,0x08,0xd3,0xe6,0xad,0x8a,0x0a,0xa2,0xac,0xe0,
- 0x14,0xc8,0xa8,0x6f,0x0a,0xa6,0x35,0xd9,0x47,0xac,0x9f,0xeb,
- 0xe8,0x3e,0xf4,0xe5,0x59,0x66,0x14,0x4b,0x2a,0x5a,0xb3,0x9d,
- 0xc1,0x38,0x14,0xb9,0x4e,0x3a,0xb6,0xe1,0x01,0xa3,0x4f,0x27
- };
-
- const uint8_t hmac_expected4[] = {
- 0x3e,0x8a,0x69,0xb7,0x78,0x3c,0x25,0x85,0x19,0x33,0xab,0x62,
- 0x90,0xaf,0x6c,0xa7,0x7a,0x99,0x81,0x48,0x08,0x50,0x00,0x9c,
- 0xc5,0x57,0x7c,0x6e,0x1f,0x57,0x3b,0x4e,0x68,0x01,0xdd,0x23,
- 0xc4,0xa7,0xd6,0x79,0xcc,0xf8,0xa3,0x86,0xc6,0x74,0xcf,0xfb,
- };
-
-// const uint8_t hmac_expected5[] = {
-// 0x3a,0xbf,0x34,0xc3,0x50,0x3b,0x2a,0x23,0xa4,0x6e,0xfc,0x61,0x9b,
-// 0xae,0xf8,0x97,
-// };
-
- const uint8_t hmac_expected6[] = {
- 0x4e,0xce,0x08,0x44,0x85,0x81,0x3e,0x90,0x88,0xd2,0xc6,0x3a,0x04,
- 0x1b,0xc5,0xb4,0x4f,0x9e,0xf1,0x01,0x2a,0x2b,0x58,0x8f,0x3c,0xd1,
- 0x1f,0x05,0x03,0x3a,0xc4,0xc6,0x0c,0x2e,0xf6,0xab,0x40,0x30,0xfe,
- 0x82,0x96,0x24,0x8d,0xf1,0x63,0xf4,0x49,0x52
- };
-
- const uint8_t hmac_expected7[] = {
- 0x66,0x17,0x17,0x8e,0x94,0x1f,0x02,0x0d,0x35,0x1e,0x2f,0x25,0x4e,
- 0x8f,0xd3,0x2c,0x60,0x24,0x20,0xfe,0xb0,0xb8,0xfb,0x9a,0xdc,0xce,
- 0xbb,0x82,0x46,0x1e,0x99,0xc5,0xa6,0x78,0xcc,0x31,0xe7,0x99,0x17,
- 0x6d,0x38,0x60,0xe6,0x11,0x0c,0x46,0x52,0x3e
- };
-
- memcpy(secret6, std::string(131, 0xaa).c_str(), 131);
- memcpy(secret7, std::string(131, 0xaa).c_str(), 131);
- RUN_NTH_TEST_CASE_FOR_ALG(1, SHA384);
- RUN_NTH_TEST_CASE_FOR_ALG(2, SHA384);
- RUN_NTH_TEST_CASE_FOR_ALG(3, SHA384);
- RUN_NTH_TEST_CASE_FOR_ALG(4, SHA384);
- RUN_NTH_TEST_CASE_FOR_ALG(6, SHA384);
- RUN_NTH_TEST_CASE_FOR_ALG(7, SHA384);
+ std::vector<std::vector<uint8_t> > hmac_expected_list(7);
+
+ int i = 0;
+ decodeHex("afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc5"
+ "9cfaea9ea9076ede7f4af152e8b2fa9cb6", hmac_expected_list[i++]);
+ decodeHex("af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec373632244"
+ "5e8e2240ca5e69e2c78b3239ecfab21649", hmac_expected_list[i++]);
+ decodeHex("88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e5596614"
+ "4b2a5ab39dc13814b94e3ab6e101a34f27", hmac_expected_list[i++]);
+ decodeHex("3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b"
+ "4e6801dd23c4a7d679ccf8a386c674cffb", hmac_expected_list[i++]);
+ decodeHex("3abf34c3503b2a23a46efc619baef897", hmac_expected_list[i++]);
+ decodeHex("4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4"
+ "c60c2ef6ab4030fe8296248df163f44952", hmac_expected_list[i++]);
+ decodeHex("6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99"
+ "c5a678cc31e799176d3860e6110c46523e", hmac_expected_list[i++]);
+
+ doRFC4231Tests(SHA384, hmac_expected_list);
}
TEST(CryptoLinkTest, HMAC_SHA512_RFC4231_SIGN) {
- const uint8_t hmac_expected1[] = {
- 0x87,0xaa,0x7c,0xde,0xa5,0xef,0x61,0x9d,0x4f,0xf0,0xb4,0x24,
- 0x1a,0x1d,0x6c,0xb0,0x23,0x79,0xf4,0xe2,0xce,0x4e,0xc2,0x78,
- 0x7a,0xd0,0xb3,0x05,0x45,0xe1,0x7c,0xde,0xda,0xa8,0x33,0xb7,
- 0xd6,0xb8,0xa7,0x02,0x03,0x8b,0x27,0x4e,0xae,0xa3,0xf4,0xe4,
- 0xbe,0x9d,0x91,0x4e,0xeb,0x61,0xf1,0x70,0x2e,0x69,0x6c,0x20,
- 0x3a,0x12,0x68,0x54
- };
- const uint8_t hmac_expected2[] = {
- 0x16,0x4b,0x7a,0x7b,0xfc,0xf8,0x19,0xe2,0xe3,0x95,0xfb,0xe7,
- 0x3b,0x56,0xe0,0xa3,0x87,0xbd,0x64,0x22,0x2e,0x83,0x1f,0xd6,
- 0x10,0x27,0x0c,0xd7,0xea,0x25,0x05,0x54,0x97,0x58,0xbf,0x75,
- 0xc0,0x5a,0x99,0x4a,0x6d,0x03,0x4f,0x65,0xf8,0xf0,0xe6,0xfd,
- 0xca,0xea,0xb1,0xa3,0x4d,0x4a,0x6b,0x4b,0x63,0x6e,0x07,0x0a,
- 0x38,0xbc,0xe7,0x37
- };
-
- const uint8_t hmac_expected3[] = {
- 0xfa,0x73,0xb0,0x08,0x9d,0x56,0xa2,0x84,0xef,0xb0,0xf0,0x75,
- 0x6c,0x89,0x0b,0xe9,0xb1,0xb5,0xdb,0xdd,0x8e,0xe8,0x1a,0x36,
- 0x55,0xf8,0x3e,0x33,0xb2,0x27,0x9d,0x39,0xbf,0x3e,0x84,0x82,
- 0x79,0xa7,0x22,0xc8,0x06,0xb4,0x85,0xa4,0x7e,0x67,0xc8,0x07,
- 0xb9,0x46,0xa3,0x37,0xbe,0xe8,0x94,0x26,0x74,0x27,0x88,0x59,
- 0xe1,0x32,0x92,0xfb
- };
-
- const uint8_t hmac_expected4[] = {
- 0xb0,0xba,0x46,0x56,0x37,0x45,0x8c,0x69,0x90,0xe5,0xa8,0xc5,
- 0xf6,0x1d,0x4a,0xf7,0xe5,0x76,0xd9,0x7f,0xf9,0x4b,0x87,0x2d,
- 0xe7,0x6f,0x80,0x50,0x36,0x1e,0xe3,0xdb,0xa9,0x1c,0xa5,0xc1,
- 0x1a,0xa2,0x5e,0xb4,0xd6,0x79,0x27,0x5c,0xc5,0x78,0x80,0x63,
- 0xa5,0xf1,0x97,0x41,0x12,0x0c,0x4f,0x2d,0xe2,0xad,0xeb,0xeb,
- 0x10,0xa2,0x98,0xdd
- };
-
-// const uint8_t hmac_expected5[] = {
-// 0x41,0x5f,0xad,0x62,0x71,0x58,0x0a,0x53,0x1d,0x41,0x79,0xbc,0x89,
-// 0x1d,0x87,0xa6,
-// };
-
- const uint8_t hmac_expected6[] = {
- 0x80,0xb2,0x42,0x63,0xc7,0xc1,0xa3,0xeb,0xb7,0x14,0x93,0xc1,0xdd,
- 0x7b,0xe8,0xb4,0x9b,0x46,0xd1,0xf4,0x1b,0x4a,0xee,0xc1,0x12,0x1b,
- 0x01,0x37,0x83,0xf8,0xf3,0x52,0x6b,0x56,0xd0,0x37,0xe0,0x5f,0x25,
- 0x98,0xbd,0x0f,0xd2,0x21,0x5d,0x6a,0x1e,0x52,0x95,0xe6,0x4f,0x73,
- 0xf6,0x3f,0x0a,0xec,0x8b,0x91,0x5a,0x98,0x5d,0x78,0x65,0x98
- };
-
- const uint8_t hmac_expected7[] = {
- 0xe3,0x7b,0x6a,0x77,0x5d,0xc8,0x7d,0xba,0xa4,0xdf,0xa9,0xf9,0x6e,
- 0x5e,0x3f,0xfd,0xde,0xbd,0x71,0xf8,0x86,0x72,0x89,0x86,0x5d,0xf5,
- 0xa3,0x2d,0x20,0xcd,0xc9,0x44,0xb6,0x02,0x2c,0xac,0x3c,0x49,0x82,
- 0xb1,0x0d,0x5e,0xeb,0x55,0xc3,0xe4,0xde,0x15,0x13,0x46,0x76,0xfb,
- 0x6d,0xe0,0x44,0x60,0x65,0xc9,0x74,0x40,0xfa,0x8c,0x6a,0x58
- };
-
- memcpy(secret6, std::string(131, 0xaa).c_str(), 131);
- memcpy(secret7, std::string(131, 0xaa).c_str(), 131);
- RUN_NTH_TEST_CASE_FOR_ALG(1, SHA512);
- RUN_NTH_TEST_CASE_FOR_ALG(2, SHA512);
- RUN_NTH_TEST_CASE_FOR_ALG(3, SHA512);
- RUN_NTH_TEST_CASE_FOR_ALG(4, SHA512);
- RUN_NTH_TEST_CASE_FOR_ALG(6, SHA512);
- RUN_NTH_TEST_CASE_FOR_ALG(7, SHA512);
+ std::vector<std::vector<uint8_t> > hmac_expected_list(7);
+
+ int i = 0;
+ decodeHex("87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17c"
+ "dedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a12"
+ "6854", hmac_expected_list[i++]);
+ decodeHex("164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505"
+ "549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bc"
+ "e737", hmac_expected_list[i++]);
+ decodeHex("fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d"
+ "39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e132"
+ "92fb", hmac_expected_list[i++]);
+ decodeHex("b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3"
+ "dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a2"
+ "98dd", hmac_expected_list[i++]);
+ decodeHex("415fad6271580a531d4179bc891d87a6", hmac_expected_list[i++]);
+ decodeHex("80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3"
+ "526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d78"
+ "6598", hmac_expected_list[i++]);
+ decodeHex("e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc9"
+ "44b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c"
+ "6a58", hmac_expected_list[i++]);
+
+ doRFC4231Tests(SHA512, hmac_expected_list);
}
TEST(CryptoLinkTest, DISABLED_HMAC_SHA256_RFC2202_SIGN_TRUNCATED) {
diff --git a/src/lib/cryptolink/tests/run_unittests.cc b/src/lib/cryptolink/tests/run_unittests.cc
index d16327e..a2181cf 100644
--- a/src/lib/cryptolink/tests/run_unittests.cc
+++ b/src/lib/cryptolink/tests/run_unittests.cc
@@ -13,10 +13,11 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index ad4374a..fbcf9c9 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -28,9 +28,11 @@ run_unittests_SOURCES += rbtree_unittest.cc
run_unittests_SOURCES += zonetable_unittest.cc
run_unittests_SOURCES += memory_datasrc_unittest.cc
run_unittests_SOURCES += logger_unittest.cc
+
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
@@ -38,6 +40,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/datasrc/tests/run_unittests.cc b/src/lib/datasrc/tests/run_unittests.cc
index a35a646..ffef333 100644
--- a/src/lib/datasrc/tests/run_unittests.cc
+++ b/src/lib/datasrc/tests/run_unittests.cc
@@ -13,6 +13,8 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+#include <log/logger_support.h>
#include <dns/tests/unittest_util.h>
@@ -21,5 +23,7 @@ main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
- return (RUN_ALL_TESTS());
+ isc::log::initLogger();
+
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index 9783beb..3a249c1 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -53,12 +53,15 @@ run_unittests_SOURCES += tsigkey_unittest.cc
run_unittests_SOURCES += tsigrecord_unittest.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+# We shouldn't need to include BOTAN_LDFLAGS here, but there
+# is one test system where the path for GTEST_LDFLAGS contains
+# an older version of botan, and somehow that version gets
+# linked if we don't
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(BOTAN_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/dns/tests/run_unittests.cc b/src/lib/dns/tests/run_unittests.cc
index 18eb0a5..7616202 100644
--- a/src/lib/dns/tests/run_unittests.cc
+++ b/src/lib/dns/tests/run_unittests.cc
@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <util/unittests/testdata.h>
#include <dns/tests/unittest_util.h>
@@ -25,5 +26,5 @@ main(int argc, char* argv[]) {
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
isc::util::unittests::addTestDataPath(TEST_DATA_BUILDDIR);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc
index 55c3ac2..ba17e70 100644
--- a/src/lib/dns/tests/tsig_unittest.cc
+++ b/src/lib/dns/tests/tsig_unittest.cc
@@ -425,6 +425,29 @@ TEST_F(TSIGTest, signUsingHMACSHA1) {
}
}
+TEST_F(TSIGTest, signUsingHMACSHA224) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4dae7d5f>;
+
+ secret.clear();
+ decodeBase64("MA+QDhXbyqUak+qnMFyTyEirzng=", secret);
+ TSIGContext sha1_ctx(TSIGKey(test_name, TSIGKey::HMACSHA224_NAME(),
+ &secret[0], secret.size()));
+
+ const uint16_t sha1_qid = 0x0967;
+ const uint8_t expected_mac[] = {
+ 0x3b, 0x93, 0xd3, 0xc5, 0xf9, 0x64, 0xb9, 0xc5, 0x00, 0x35,
+ 0x02, 0x69, 0x9f, 0xfc, 0x44, 0xd6, 0xe2, 0x66, 0xf4, 0x08,
+ 0xef, 0x33, 0xa2, 0xda, 0xa1, 0x48, 0x71, 0xd3
+ };
+ {
+ SCOPED_TRACE("Sign test using HMAC-SHA1");
+ commonSignChecks(createMessageAndSign(sha1_qid, test_name, &sha1_ctx),
+ sha1_qid, 0x4dae7d5f, expected_mac,
+ sizeof(expected_mac), 0, 0, NULL,
+ TSIGKey::HMACSHA224_NAME());
+ }
+}
+
// The first part of this test checks verifying the signed query used for
// the "sign" test.
// The second part of this test generates a signed response to the signed
diff --git a/src/lib/exceptions/tests/run_unittests.cc b/src/lib/exceptions/tests/run_unittests.cc
index 0908071..6a0de4f 100644
--- a/src/lib/exceptions/tests/run_unittests.cc
+++ b/src/lib/exceptions/tests/run_unittests.cc
@@ -17,5 +17,8 @@
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
+
+ // Unlike other tests we cannot use our wrapper for RUN_ALL_TESTS()
+ // due to dependency.
return (RUN_ALL_TESTS());
}
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index c27b3e4..7833e0d 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -2,32 +2,36 @@ SUBDIRS = . compiler tests
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = liblog.la
liblog_la_SOURCES =
-liblog_la_SOURCES += debug_levels.h logger_levels.h
liblog_la_SOURCES += dummylog.h dummylog.cc
+liblog_la_SOURCES += impldef.cc impldef.h
+liblog_la_SOURCES += log_formatter.h log_formatter.cc
liblog_la_SOURCES += logger.cc logger.h
liblog_la_SOURCES += logger_impl.cc logger_impl.h
+liblog_la_SOURCES += logger_level.h
+liblog_la_SOURCES += logger_level.cc logger_level.h
+liblog_la_SOURCES += logger_level_impl.cc logger_level_impl.h
+liblog_la_SOURCES += logger_manager.cc logger_manager.h
+liblog_la_SOURCES += logger_manager_impl.cc logger_manager_impl.h
+liblog_la_SOURCES += logger_name.cc logger_name.h
+liblog_la_SOURCES += logger_specification.h
liblog_la_SOURCES += logger_support.cc logger_support.h
+liblog_la_SOURCES += macros.h
liblog_la_SOURCES += messagedef.cc messagedef.h
liblog_la_SOURCES += message_dictionary.cc message_dictionary.h
liblog_la_SOURCES += message_exception.h
liblog_la_SOURCES += message_initializer.cc message_initializer.h
liblog_la_SOURCES += message_reader.cc message_reader.h
liblog_la_SOURCES += message_types.h
-liblog_la_SOURCES += root_logger_name.cc root_logger_name.h
-liblog_la_SOURCES += log_formatter.h log_formatter.cc
-liblog_la_SOURCES += macros.h
+liblog_la_SOURCES += output_option.cc output_option.h
EXTRA_DIST = README
+EXTRA_DIST += impldef.mes
EXTRA_DIST += messagedef.mes
-EXTRA_DIST += logger_impl_log4cxx.cc logger_impl_log4cxx.h
-EXTRA_DIST += xdebuglevel.cc xdebuglevel.h
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
@@ -39,5 +43,6 @@ if USE_CLANGPP
# Same for clang++, but we need to turn off -Werror completely.
liblog_la_CXXFLAGS += -Wno-error
endif
-liblog_la_CPPFLAGS = $(AM_CPPFLAGS)
-liblog_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
+liblog_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+liblog_la_LDFLAGS = $(LOG4CPLUS_LDFLAGS)
+liblog_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
diff --git a/src/lib/log/README b/src/lib/log/README
index 529eefc..6b4cf11 100644
--- a/src/lib/log/README
+++ b/src/lib/log/README
@@ -117,7 +117,7 @@ Points to note:
* Lines starting $ are directives. At present, two directives are recognised:
* $PREFIX, which has one optional argument: the string used to prefix symbols.
- If absent, there is no prefix to the symbols. (Prefixes are explained below.)
+ If absent, there is no prefix to the symbols (prefixes are explained below).
* $NAMESPACE, which has one argument: the namespace in which the symbols are
created. In the absence of a $NAMESPACE directive, symbols will be put in
@@ -135,7 +135,7 @@ Points to note:
* The replacement tokens are the strings "%1", "%2" etc. When a message
is logged, these are replaced with the arguments passed to the logging
call: %1 refers to the first argument, %2 to the second etc. Within the
- message text, the placeholders can appear in any order, and placeholders
+ message text, the placeholders can appear in any order and placeholders
can be repeated.
* Remaining lines indicate an explanation for the preceding message. These
@@ -215,33 +215,9 @@ To use the current version of the logging:
1. Build message header file and source file as describe above.
-2. In the main module of the program, declare an instance of the
- RootLoggerName class to define the name of the program's root logger, e.g.
-
- #include <log/root_logger_name.h>
-
- isc::log::RootLoggerName("b10-auth");
-
- This can be declared inside or outside an execution unit.
-
-2. In the code that needs to do logging, declare a logger with a given name,
- e.g.
-
- #include <log/logger.h>
- :
- isc::log::Logger logger("myname"); // "myname" can be anything
-
- The above example assumes declaration outside a function. If declaring
- non-statically within a function, declare it as:
-
- isc::log::Logger logger("myname", true);
-
- (The argument is required to support a possible future implementation of
- logging. Currently it has no effect.)
-
-3. The main program unit should include a call to isc::log::initLogger()
+2. The main program unit should include a call to isc::log::initLogger()
(defined in logger_support.h) to set the logging severity, debug log level,
- and external message file.
+ and external message file:
a) The logging severity is one of the enum defined in logger.h, i.e.
@@ -262,56 +238,43 @@ To use the current version of the logging:
directive of a particular type will be ignored; multiple directives will
cause the read of the file to fail with an error.)
-4. Issue logging calls using methods on logger, e.g.
+ The settings remain in effect until the logging configuration is read, and
+ so provide the default logging during program initialization.
- logger.error(DPS_NSTIMEOUT).arg("isc.org");
+3. Issue logging calls using supplied macros in "log/macros.h", e.g.
- (where, in the example above we might have defined the symbol in the message
+ LOG_ERROR(logger, DPS_NSTIMEOUT).arg("isc.org");
+
+ (The macros are more efficient that calls to the methods on the logger class:
+ they avoid the overhead of evaluating the parameters to arg() if the
+ settings are such that the message is not going to be output.)
+
+ Note: in the example above we might have defined the symbol in the message
file with something along the lines of:
$PREFIX DPS_
:
NSTIMEOUT queries to all nameservers for %1 have timed out
- At present, the only logging is to the console.
-
-
-Efficiency Considerations
--------------------------
-A common pattern in logging is a debug call of the form:
-
- logger.debug(dbglevel, MSGID).arg(expensive_call()).arg(...
-
-... where "expensive_call()" is a function call to obtain logging information
-that may be computationally intensive. Although the cost may be justified
-when debugging is enabled, the cost is still incurred even if debugging is
-disabled and the debug() method returns without outputting anything. (The
-same may be true of other logging levels, although there are likely to be
-fewer calls to logger.info(), logger.error() etc. throughout the code and
-they are less likely to be disabled.)
-
-For this reason, a set of macros is provided and are called using the
-construct:
-
- LOG_DEBUG(logger, dbglevel, MSGID).arg(expensive_call()).arg(...
- LOG_INFO(logger, MSGID).arg(expensive_call()...)
-
-If these are used, the arguments passed to the arg() method are not evaluated
-if the relevant logging level is disabled.
-
-
Severity Guidelines
===================
When using logging, the question arises, what severity should a message be
logged at? The following is a suggestion - as always, the decision must be
made in the context of which the message is logged.
+One thing that should always be borne in mind is whether the logging could
+be used as a vector for a DOS attack. For example, if a warning message is
+logged every time an invalid packet is received, an attacker could simply send
+large numbers of invalid packets. (Of course, warnings could be disabled (or
+just warnings for that that particular logger), but nevertheless the message
+is an attack vector.)
+
FATAL
-----
-The program has encountered an error that is so severe that it cannot
-continue (or there is no point in continuing). When a fatal error has been
-logged, the program will usually exit immediately (via a call to abort()) or
-shortly afterwards, after dumping some diagnostic information.
+The program has encountered an error that is so severe that it cannot continue
+(or there is no point in continuing). When a fatal error has been logged,
+the program will usually exit immediately (or shortly afterwards) after
+dumping some diagnostic information.
ERROR
-----
@@ -342,7 +305,7 @@ are distributed between the levels is up to the developer. So if debugging
the NSAS (for example), a level 0 message might record the creation of a new
zone, a level 10 recording a timeout when trying to get a nameserver address,
but a level 50 would record every query for an address. (And we might add
-level 51 to record every update of the RTT.)
+level 70 to record every update of the RTT.)
Note that like severities, levels are cumulative; so if level 25 is set as the
debug level, all debug levels from 0 to 25 will be output. In fact, it is
@@ -397,13 +360,3 @@ is only one piece of code that does this functionality.
Outstanding Issues
==================
* Ability to configure system according to configuration database.
-
-
-log4cxx Issues
-==============
-Some experimental code to utilise log4cxx as an underlying implementation
-is present in the source code directory although it is not currently used.
-The files are:
-
- logger_impl_log4cxx.{cc,h}
- xdebuglevel.{cc,h}
diff --git a/src/lib/log/compiler/Makefile.am b/src/lib/log/compiler/Makefile.am
index d51ba05..1f47ba9 100644
--- a/src/lib/log/compiler/Makefile.am
+++ b/src/lib/log/compiler/Makefile.am
@@ -1,8 +1,6 @@
SUBDIRS = .
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -14,7 +12,7 @@ endif
CLEANFILES = *.gcno *.gcda
noinst_PROGRAMS = message
-message_SOURCES = message.cc
-message_LDADD = $(top_builddir)/src/lib/log/liblog.la
-message_LDADD += $(top_builddir)/src/lib/util/libutil.la
+message_SOURCES = message.cc
+message_LDADD = $(top_builddir)/src/lib/log/liblog.la
+message_LDADD += $(top_builddir)/src/lib/util/libutil.la
diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc
index 457a62e..155aa55 100644
--- a/src/lib/log/compiler/message.cc
+++ b/src/lib/log/compiler/message.cc
@@ -35,6 +35,8 @@
#include <log/logger.h>
+#include <boost/foreach.hpp>
+
using namespace std;
using namespace isc::log;
using namespace isc::util;
@@ -78,10 +80,11 @@ version() {
void
usage() {
cout <<
- "Usage: message [-h] [-v] <message-file>\n" <<
+ "Usage: message [-h] [-v] [-p] <message-file>\n" <<
"\n" <<
"-h Print this message and exit\n" <<
"-v Print the program version and exit\n" <<
+ "-p Output python source instead of C++ ones\n" <<
"\n" <<
"<message-file> is the name of the input message file.\n";
}
@@ -237,6 +240,44 @@ writeClosingNamespace(ostream& output, const vector<string>& ns) {
}
}
+/// \breif Write python file
+///
+/// Writes the python file containing the symbol definitions as module level
+/// constants. These are objects which register themself at creation time,
+/// so they can be replaced by dictionary later.
+///
+/// \param file Name of the message file. The source code is written to a file
+/// file of the same name but with a .py suffix.
+/// \param dictionary The dictionary holding the message definitions.
+///
+/// \note We don't use the namespace as in C++. We don't need it, because
+/// python file/module works as implicit namespace as well.
+
+void
+writePythonFile(const string& file, MessageDictionary& dictionary) {
+ Filename message_file(file);
+ Filename python_file(Filename(message_file.name()).useAsDefault(".py"));
+
+ // Open the file for writing
+ ofstream pyfile(python_file.fullName().c_str());
+
+ // Write the comment and imports
+ pyfile <<
+ "# File created from " << message_file.fullName() << " on " <<
+ currentTime() << "\n" <<
+ "\n" <<
+ "import isc.log.message\n" <<
+ "\n";
+
+ vector<string> idents(sortedIdentifiers(dictionary));
+ BOOST_FOREACH(const string& ident, idents) {
+ pyfile << ident << " = isc.log.message.create(\"" <<
+ ident << "\", \"" << quoteString(dictionary.getText(ident)) <<
+ "\")\n";
+ }
+
+ pyfile.close();
+}
/// \brief Write Header File
///
@@ -264,52 +305,46 @@ writeHeaderFile(const string& file, const vector<string>& ns_components,
// Open the output file for writing
ofstream hfile(header_file.fullName().c_str());
- try {
- if (hfile.fail()) {
- throw MessageException(MSG_OPENOUT, header_file.fullName(),
- strerror(errno));
- }
-
- // Write the header preamble. If there is an error, we'll pick it up
- // after the last write.
-
- hfile <<
- "// File created from " << message_file.fullName() << " on " <<
- currentTime() << "\n" <<
- "\n" <<
- "#ifndef " << sentinel_text << "\n" <<
- "#define " << sentinel_text << "\n" <<
- "\n" <<
- "#include <log/message_types.h>\n" <<
- "\n";
-
- // Write the message identifiers, bounded by a namespace declaration
- writeOpeningNamespace(hfile, ns_components);
-
- vector<string> idents = sortedIdentifiers(dictionary);
- for (vector<string>::const_iterator j = idents.begin();
- j != idents.end(); ++j) {
- hfile << "extern const isc::log::MessageID " << *j << ";\n";
- }
- hfile << "\n";
+ if (hfile.fail()) {
+ throw MessageException(MSG_OPENOUT, header_file.fullName(),
+ strerror(errno));
+ }
- writeClosingNamespace(hfile, ns_components);
+ // Write the header preamble. If there is an error, we'll pick it up
+ // after the last write.
+
+ hfile <<
+ "// File created from " << message_file.fullName() << " on " <<
+ currentTime() << "\n" <<
+ "\n" <<
+ "#ifndef " << sentinel_text << "\n" <<
+ "#define " << sentinel_text << "\n" <<
+ "\n" <<
+ "#include <log/message_types.h>\n" <<
+ "\n";
+
+ // Write the message identifiers, bounded by a namespace declaration
+ writeOpeningNamespace(hfile, ns_components);
+
+ vector<string> idents = sortedIdentifiers(dictionary);
+ for (vector<string>::const_iterator j = idents.begin();
+ j != idents.end(); ++j) {
+ hfile << "extern const isc::log::MessageID " << *j << ";\n";
+ }
+ hfile << "\n";
- // ... and finally the postamble
- hfile << "#endif // " << sentinel_text << "\n";
+ writeClosingNamespace(hfile, ns_components);
- // Report errors (if any) and exit
- if (hfile.fail()) {
- throw MessageException(MSG_WRITERR, header_file.fullName(),
- strerror(errno));
- }
+ // ... and finally the postamble
+ hfile << "#endif // " << sentinel_text << "\n";
- hfile.close();
- }
- catch (MessageException&) {
- hfile.close();
- throw;
+ // Report errors (if any) and exit
+ if (hfile.fail()) {
+ throw MessageException(MSG_WRITERR, header_file.fullName(),
+ strerror(errno));
}
+
+ hfile.close();
}
@@ -357,76 +392,71 @@ writeProgramFile(const string& file, const vector<string>& ns_components,
// Open the output file for writing
ofstream ccfile(program_file.fullName().c_str());
- try {
- if (ccfile.fail()) {
- throw MessageException(MSG_OPENOUT, program_file.fullName(),
- strerror(errno));
- }
- // Write the preamble. If there is an error, we'll pick it up after
- // the last write.
+ if (ccfile.fail()) {
+ throw MessageException(MSG_OPENOUT, program_file.fullName(),
+ strerror(errno));
+ }
- ccfile <<
- "// File created from " << message_file.fullName() << " on " <<
- currentTime() << "\n" <<
- "\n" <<
- "#include <cstddef>\n" <<
- "#include <log/message_types.h>\n" <<
- "#include <log/message_initializer.h>\n" <<
- "\n";
+ // Write the preamble. If there is an error, we'll pick it up after
+ // the last write.
- // Declare the message symbols themselves.
+ ccfile <<
+ "// File created from " << message_file.fullName() << " on " <<
+ currentTime() << "\n" <<
+ "\n" <<
+ "#include <cstddef>\n" <<
+ "#include <log/message_types.h>\n" <<
+ "#include <log/message_initializer.h>\n" <<
+ "\n";
- writeOpeningNamespace(ccfile, ns_components);
+ // Declare the message symbols themselves.
- vector<string> idents = sortedIdentifiers(dictionary);
- for (vector<string>::const_iterator j = idents.begin();
- j != idents.end(); ++j) {
- ccfile << "extern const isc::log::MessageID " << *j <<
- " = \"" << *j << "\";\n";
- }
- ccfile << "\n";
+ writeOpeningNamespace(ccfile, ns_components);
- writeClosingNamespace(ccfile, ns_components);
+ vector<string> idents = sortedIdentifiers(dictionary);
+ for (vector<string>::const_iterator j = idents.begin();
+ j != idents.end(); ++j) {
+ ccfile << "extern const isc::log::MessageID " << *j <<
+ " = \"" << *j << "\";\n";
+ }
+ ccfile << "\n";
- // Now the code for the message initialization.
+ writeClosingNamespace(ccfile, ns_components);
- ccfile <<
- "namespace {\n" <<
- "\n" <<
- "const char* values[] = {\n";
+ // Now the code for the message initialization.
- // Output the identifiers and the associated text.
- idents = sortedIdentifiers(dictionary);
- for (vector<string>::const_iterator i = idents.begin();
- i != idents.end(); ++i) {
- ccfile << " \"" << *i << "\", \"" <<
- quoteString(dictionary.getText(*i)) << "\",\n";
- }
+ ccfile <<
+ "namespace {\n" <<
+ "\n" <<
+ "const char* values[] = {\n";
+ // Output the identifiers and the associated text.
+ idents = sortedIdentifiers(dictionary);
+ for (vector<string>::const_iterator i = idents.begin();
+ i != idents.end(); ++i) {
+ ccfile << " \"" << *i << "\", \"" <<
+ quoteString(dictionary.getText(*i)) << "\",\n";
+ }
- // ... and the postamble
- ccfile <<
- " NULL\n" <<
- "};\n" <<
- "\n" <<
- "const isc::log::MessageInitializer initializer(values);\n" <<
- "\n" <<
- "} // Anonymous namespace\n" <<
- "\n";
-
- // Report errors (if any) and exit
- if (ccfile.fail()) {
- throw MessageException(MSG_WRITERR, program_file.fullName(),
- strerror(errno));
- }
- ccfile.close();
- }
- catch (MessageException&) {
- ccfile.close();
- throw;
+ // ... and the postamble
+ ccfile <<
+ " NULL\n" <<
+ "};\n" <<
+ "\n" <<
+ "const isc::log::MessageInitializer initializer(values);\n" <<
+ "\n" <<
+ "} // Anonymous namespace\n" <<
+ "\n";
+
+ // Report errors (if any) and exit
+ if (ccfile.fail()) {
+ throw MessageException(MSG_WRITERR, program_file.fullName(),
+ strerror(errno));
}
+
+ ccfile.close();
}
@@ -466,13 +496,19 @@ warnDuplicates(MessageReader& reader) {
int
main(int argc, char* argv[]) {
- const char* soptions = "hv"; // Short options
+ const char* soptions = "hvp"; // Short options
optind = 1; // Ensure we start a new scan
int opt; // Value of the option
+ bool doPython = false;
+
while ((opt = getopt(argc, argv, soptions)) != -1) {
switch (opt) {
+ case 'p':
+ doPython = true;
+ break;
+
case 'h':
usage();
return 0;
@@ -508,15 +544,27 @@ main(int argc, char* argv[]) {
MessageReader reader(&dictionary);
reader.readFile(message_file);
- // Get the namespace into which the message definitions will be put and
- // split it into components.
- vector<string> ns_components = splitNamespace(reader.getNamespace());
-
- // Write the header file.
- writeHeaderFile(message_file, ns_components, dictionary);
-
- // Write the file that defines the message symbols and text
- writeProgramFile(message_file, ns_components, dictionary);
+ if (doPython) {
+ // Warn in case of ignored directives
+ if (!reader.getNamespace().empty()) {
+ cerr << "Python mode, ignoring the $NAMESPACE directive" <<
+ endl;
+ }
+
+ // Write the whole python file
+ writePythonFile(message_file, dictionary);
+ } else {
+ // Get the namespace into which the message definitions will be put and
+ // split it into components.
+ vector<string> ns_components =
+ splitNamespace(reader.getNamespace());
+
+ // Write the header file.
+ writeHeaderFile(message_file, ns_components, dictionary);
+
+ // Write the file that defines the message symbols and text
+ writeProgramFile(message_file, ns_components, dictionary);
+ }
// Finally, warn of any duplicates encountered.
warnDuplicates(reader);
diff --git a/src/lib/log/debug_levels.h b/src/lib/log/debug_levels.h
deleted file mode 100644
index bb2b524..0000000
--- a/src/lib/log/debug_levels.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __DEBUG_LEVELS_H
-#define __DEBUG_LEVELS_H
-
-/// \brief Defines Debug Levels
-///
-/// Defines the maximum and minimum debug levels and the number of levels.
-/// These are defined using #define as they are referenced in the construction
-/// of variables declared outside execution units. (In this way we avoid the
-/// "static initialization fiasco" problem.)
-
-#define MIN_DEBUG_LEVEL (0)
-#define MAX_DEBUG_LEVEL (99)
-#define NUM_DEBUG_LEVEL (MAX_DEBUG_LEVEL - MIN_DEBUG_LEVEL + 1)
-
-#endif // __DEBUG_LEVELS_H
diff --git a/src/lib/log/impldef.cc b/src/lib/log/impldef.cc
new file mode 100644
index 0000000..087ebea
--- /dev/null
+++ b/src/lib/log/impldef.cc
@@ -0,0 +1,29 @@
+// File created from impldef.mes on Wed Jun 1 10:32:57 2011
+
+#include <cstddef>
+#include <log/message_types.h>
+#include <log/message_initializer.h>
+
+namespace isc {
+namespace log {
+
+extern const isc::log::MessageID LOGIMPL_ABOVEDBGMAX = "LOGIMPL_ABOVEDBGMAX";
+extern const isc::log::MessageID LOGIMPL_BADDEBUG = "LOGIMPL_BADDEBUG";
+extern const isc::log::MessageID LOGIMPL_BELOWDBGMIN = "LOGIMPL_BELOWDBGMIN";
+
+} // namespace log
+} // namespace isc
+
+namespace {
+
+const char* values[] = {
+ "LOGIMPL_ABOVEDBGMAX", "debug level of %1 is too high and will be set to the maximum of %2",
+ "LOGIMPL_BADDEBUG", "debug string is '%1': must be of the form DEBUGn",
+ "LOGIMPL_BELOWDBGMIN", "debug level of %1 is too low and will be set to the minimum of %2",
+ NULL
+};
+
+const isc::log::MessageInitializer initializer(values);
+
+} // Anonymous namespace
+
diff --git a/src/lib/log/impldef.h b/src/lib/log/impldef.h
new file mode 100644
index 0000000..7c70996
--- /dev/null
+++ b/src/lib/log/impldef.h
@@ -0,0 +1,18 @@
+// File created from impldef.mes on Wed Jun 1 10:32:57 2011
+
+#ifndef __IMPLDEF_H
+#define __IMPLDEF_H
+
+#include <log/message_types.h>
+
+namespace isc {
+namespace log {
+
+extern const isc::log::MessageID LOGIMPL_ABOVEDBGMAX;
+extern const isc::log::MessageID LOGIMPL_BADDEBUG;
+extern const isc::log::MessageID LOGIMPL_BELOWDBGMIN;
+
+} // namespace log
+} // namespace isc
+
+#endif // __IMPLDEF_H
diff --git a/src/lib/log/impldef.mes b/src/lib/log/impldef.mes
new file mode 100644
index 0000000..93e9fab
--- /dev/null
+++ b/src/lib/log/impldef.mes
@@ -0,0 +1,38 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# \brief Logger Implementation Messages
+#
+# This holds messages generated by the underlying logger implementation. They
+# are likely to be specific to that implementation, and may well change if the
+# underlying implementation is changed. For that reason, they have been put
+# in a separate file.
+
+$PREFIX LOGIMPL_
+$NAMESPACE isc::log
+
+% ABOVEDBGMAX debug level of %1 is too high and will be set to the maximum of %2
+A message from the underlying logger implementation code, the debug level
+(as set by the string DEBGUGn) is above the maximum allowed value and has
+been reduced to that value.
+
+% BADDEBUG debug string is '%1': must be of the form DEBUGn
+The string indicating the extended logging level (used by the underlying
+logger implementation code) is not of the stated form. In particular,
+it starts DEBUG but does not end with an integer.
+
+% BELOWDBGMIN debug level of %1 is too low and will be set to the minimum of %2
+A message from the underlying logger implementation code, the debug level
+(as set by the string DEBGUGn) is below the minimum allowed value and has
+been increased to that value.
diff --git a/src/lib/log/log_formatter.h b/src/lib/log/log_formatter.h
index cda1d96..c81d4ea 100644
--- a/src/lib/log/log_formatter.h
+++ b/src/lib/log/log_formatter.h
@@ -15,8 +15,11 @@
#ifndef __LOG_FORMATTER_H
#define __LOG_FORMMATER_H
+#include <cstddef>
#include <string>
+#include <iostream>
#include <boost/lexical_cast.hpp>
+#include <log/logger_level.h>
namespace isc {
namespace log {
@@ -73,13 +76,17 @@ private:
///
/// If NULL, we are not active and should not produce anything.
mutable Logger* logger_;
- /// \brief Prefix (eg. "ERROR", "DEBUG" or like that)
- const char* prefix_;
+
+ /// \brief Message severity
+ Severity severity_;
+
/// \brief The messages with %1, %2... placeholders
std::string* message_;
+
/// \brief Which will be the next placeholder to replace
unsigned nextPlaceholder_;
- Formatter& operator =(const Formatter& other);
+
+
public:
/// \brief Constructor of "active" formatter
///
@@ -89,34 +96,57 @@ public:
///
/// It is not expected to be called by user of logging system directly.
///
- /// \param prefix The severity prefix, like "ERROR" or "DEBUG"
+ /// \param severity The severity of the message (DEBUG, ERROR etc.)
/// \param message The message with placeholders. We take ownership of
/// it and we will modify the string. Must not be NULL unless
/// logger is also NULL, but it's not checked.
/// \param logger The logger where the final output will go, or NULL
/// if no output is wanted.
- Formatter(const char* prefix = NULL, std::string* message = NULL,
+ Formatter(const Severity& severity = NONE, std::string* message = NULL,
Logger* logger = NULL) :
- logger_(logger), prefix_(prefix), message_(message),
- nextPlaceholder_(1)
+ logger_(logger), severity_(severity), message_(message),
+ nextPlaceholder_(0)
{
}
+ /// \brief Copy constructor
+ ///
+ /// "Control" is passed to the created object in that it is the created object
+ /// that will have responsibility for outputting the formatted message - the
+ /// object being copied relinquishes that responsibility.
Formatter(const Formatter& other) :
- logger_(other.logger_), prefix_(other.prefix_),
+ logger_(other.logger_), severity_(other.severity_),
message_(other.message_), nextPlaceholder_(other.nextPlaceholder_)
{
- other.logger_ = false;
+ other.logger_ = NULL;
}
+
/// \brief Destructor.
//
/// This is the place where output happens if the formatter is active.
~ Formatter() {
if (logger_) {
- logger_->output(prefix_, *message_);
+ logger_->output(severity_, *message_);
delete message_;
}
}
+
+ /// \brief Assignment operator
+ ///
+ /// Essentially the same function as the assignment operator - the object being
+ /// assigned to takes responsibility for outputting the message.
+ Formatter& operator =(const Formatter& other) {
+ if (&other != this) {
+ logger_ = other.logger_;
+ severity_ = other.severity_;
+ message_ = other.message_;
+ nextPlaceholder_ = other.nextPlaceholder_;
+ other.logger_ = NULL;
+ }
+
+ return *this;
+ }
+
/// \brief Replaces another placeholder
///
/// Replaces another placeholder and returns a new formatter with it.
@@ -131,17 +161,25 @@ public:
return (*this);
}
}
+
/// \brief String version of arg.
Formatter& arg(const std::string& arg) {
if (logger_) {
- // FIXME: This logic has a problem. If we had a message like
- // "%1 %2" and called .arg("%2").arg(42), we would get "42 %2".
- // But we consider this to be rare enough not to complicate
- // matters.
- replacePlaceholder(message_, arg, nextPlaceholder_ ++);
+ // Note that this method does a replacement and returns the
+ // modified string. If there are multiple invocations of arg() (e.g.
+ // logger.info(msgid).arg(xxx).arg(yyy)...), each invocation
+ // operates on the string returned by the previous one. This
+ // sequential operation means that if we had a message like "%1 %2",
+ // and called .arg("%2").arg(42), we would get "42 42"; the first
+ // call replaces the %1" with "%2" and the second replaces all
+ // occurrences of "%2" with 42. (Conversely, the sequence
+ // .arg(42).arg("%1") would return "42 %1" - there are no recursive
+ // replacements).
+ replacePlaceholder(message_, arg, ++nextPlaceholder_ );
}
return (*this);
}
+
};
}
diff --git a/src/lib/log/logger.cc b/src/lib/log/logger.cc
index c340315..5a35d36 100644
--- a/src/lib/log/logger.cc
+++ b/src/lib/log/logger.cc
@@ -17,9 +17,9 @@
#include <log/logger.h>
#include <log/logger_impl.h>
+#include <log/logger_name.h>
#include <log/message_dictionary.h>
#include <log/message_types.h>
-#include <log/root_logger_name.h>
#include <util/strutil.h>
@@ -28,10 +28,9 @@ using namespace std;
namespace isc {
namespace log {
-// Initialize Logger implementation. Does not check whether the implementation
-// has already been initialized - that was done by the caller (getLoggerPtr()).
+// Initialize Logger.
void Logger::initLoggerImpl() {
- loggerptr_ = new LoggerImpl(name_, infunc_);
+ loggerptr_ = new LoggerImpl(name_);
}
// Destructor.
@@ -75,6 +74,14 @@ Logger::getDebugLevel() {
return (getLoggerPtr()->getDebugLevel());
}
+// Effective debug level (only relevant if messages of severity DEBUG are being
+// logged).
+
+int
+Logger::getEffectiveDebugLevel() {
+ return (getLoggerPtr()->getEffectiveDebugLevel());
+}
+
// Check on the current severity settings
bool
@@ -112,14 +119,14 @@ Logger::isFatalEnabled() {
// Output methods
void
-Logger::output(const char* sevText, const string& message) {
- getLoggerPtr()->outputRaw(sevText, message);
+Logger::output(const Severity& severity, const std::string& message) {
+ getLoggerPtr()->outputRaw(severity, message);
}
Logger::Formatter
Logger::debug(int dbglevel, const isc::log::MessageID& ident) {
if (isDebugEnabled(dbglevel)) {
- return (Formatter("DEBUG", getLoggerPtr()->lookupMessage(ident),
+ return (Formatter(DEBUG, getLoggerPtr()->lookupMessage(ident),
this));
} else {
return (Formatter());
@@ -129,7 +136,7 @@ Logger::debug(int dbglevel, const isc::log::MessageID& ident) {
Logger::Formatter
Logger::info(const isc::log::MessageID& ident) {
if (isInfoEnabled()) {
- return (Formatter("INFO ", getLoggerPtr()->lookupMessage(ident),
+ return (Formatter(INFO, getLoggerPtr()->lookupMessage(ident),
this));
} else {
return (Formatter());
@@ -139,7 +146,7 @@ Logger::info(const isc::log::MessageID& ident) {
Logger::Formatter
Logger::warn(const isc::log::MessageID& ident) {
if (isWarnEnabled()) {
- return (Formatter("WARN ", getLoggerPtr()->lookupMessage(ident),
+ return (Formatter(WARN, getLoggerPtr()->lookupMessage(ident),
this));
} else {
return (Formatter());
@@ -149,7 +156,7 @@ Logger::warn(const isc::log::MessageID& ident) {
Logger::Formatter
Logger::error(const isc::log::MessageID& ident) {
if (isErrorEnabled()) {
- return (Formatter("ERROR", getLoggerPtr()->lookupMessage(ident),
+ return (Formatter(ERROR, getLoggerPtr()->lookupMessage(ident),
this));
} else {
return (Formatter());
@@ -159,22 +166,18 @@ Logger::error(const isc::log::MessageID& ident) {
Logger::Formatter
Logger::fatal(const isc::log::MessageID& ident) {
if (isFatalEnabled()) {
- return (Formatter("FATAL", getLoggerPtr()->lookupMessage(ident),
+ return (Formatter(FATAL, getLoggerPtr()->lookupMessage(ident),
this));
} else {
return (Formatter());
}
}
-bool Logger::operator==(Logger& other) {
- return (*getLoggerPtr() == *other.getLoggerPtr());
-}
-
-// Protected methods (used for testing)
+// Comparison (testing only)
-void
-Logger::reset() {
- LoggerImpl::reset();
+bool
+Logger::operator==(Logger& other) {
+ return (*getLoggerPtr() == *other.getLoggerPtr());
}
} // namespace log
diff --git a/src/lib/log/logger.h b/src/lib/log/logger.h
index 6bd8924..378db10 100644
--- a/src/lib/log/logger.h
+++ b/src/lib/log/logger.h
@@ -18,34 +18,73 @@
#include <cstdlib>
#include <string>
-#include <log/debug_levels.h>
-#include <log/logger_levels.h>
+#include <log/logger_level.h>
#include <log/message_types.h>
#include <log/log_formatter.h>
namespace isc {
namespace log {
-/// \brief Logging API
-///
-/// This module forms the interface into the logging subsystem. Features of the
-/// system and its implementation are:
-///
-/// # Multiple logging objects can be created, each given a name; those with the
-/// same name share characteristics (like destination, level being logged
-/// etc.)
-/// # Messages can be logged at severity levels of FATAL, ERROR, WARN, INFO or
-/// DEBUG. The DEBUG level has further sub-levels numbered 0 (least
-/// informative) to 99 (most informative).
-/// # Each logger has a severity level set associated with it. When a message
-/// is logged, it is output only if it is logged at a level equal to the
-/// logger severity level or greater, e.g. if the logger's severity is WARN,
-/// only messages logged at WARN, ERROR or FATAL will be output.
-/// # Messages are identified by message identifiers, which are keys into a
-/// message dictionary.
+/// \page LoggingApi Logging API
+/// \section LoggingApiOverview Overview
+/// BIND 10 logging uses the concepts of the widely-used Java logging
+/// package log4j (http://logging.apache.log/log4j), albeit implemented
+/// in C++ using an open-source port. Features of the system are:
+///
+/// - Within the code objects - known as loggers - can be created and
+/// used to log messages. These loggers have names; those with the
+/// same name share characteristics (such as output destination).
+/// - Loggers have a hierarchical relationship, with each logger being
+/// the child of another logger, except for the top of the hierarchy, the
+/// root logger. If a logger does not log a message, it is passed to the
+/// parent logger.
+/// - Messages can be logged at severity levels of FATAL, ERROR, WARN, INFO
+/// or DEBUG. The DEBUG level has further sub-levels numbered 0 (least
+/// informative) to 99 (most informative).
+/// - Each logger has a severity level set associated with it. When a
+/// message is logged, it is output only if it is logged at a level equal
+/// to the logger severity level or greater, e.g. if the logger's severity
+/// is WARN, only messages logged at WARN, ERROR or FATAL will be output.
+///
+/// \section LoggingApiLoggerNames BIND 10 Logger Names
+/// Within BIND 10, the root logger root logger is given the name of the
+/// program (via the stand-alone function setRootLoggerName()). Other loggers
+/// are children of the root logger and are named "<program>.<sublogger>".
+/// This name appears in logging output, allowing users to identify both
+/// the BIND 10 program and the component within the program that generated
+/// the message.
+///
+/// When creating a logger, the abbreviated name "<sublogger>" can be used;
+/// the program name will be prepended to it when the logger is created.
+/// In this way, individual libraries can have their own loggers without
+/// worrying about the program in which they are used, but:
+/// - The origin of the message will be clearly identified.
+/// - The same component can have different options (e.g. logging severity)
+/// in different programs at the same time.
+///
+/// \section LoggingApiLoggingMessages Logging Messages
+/// Instead of embedding the text of messages within the code, each message
+/// is referred to using a symbolic name. The logging code uses this name as
+/// a key in a dictionary from which the message text is obtained. Such a
+/// system allows for the optional replacement of message text at run time.
+/// More details about the message disction (and the compiler used to create
+/// the symbol definitions) can be found in other modules in the src/lib/log
+/// directory.
class LoggerImpl; // Forward declaration of the implementation class
+/// \brief Logger Class
+///
+/// This class is the main class used for logging. Use comprises:
+///
+/// 1. Constructing a logger by instantiating it with a specific name. (If the
+/// same logger is in multiple functions within a file, overhead can be
+/// minimised by declaring it as a file-wide static variable.)
+/// 2. Using the error(), info() etc. methods to log an error. (However, it is
+/// recommended to use the LOG_ERROR, LOG_INFO etc. macros defined in macros.h.
+/// These will avoid the potentially-expensive evaluation of arguments if the
+/// severity is such that the message will be suppressed.)
+
class Logger {
public:
@@ -56,32 +95,7 @@ public:
/// \param name Name of the logger. If the name is that of the root name,
/// this creates an instance of the root logger; otherwise it creates a
/// child of the root logger.
- ///
- /// \param infunc This argument is present to get round a bug in some
- /// implementations of the logging system. If the logger is declared in
- /// a function (such that it will be deleted when the function exits,
- /// before the program ends), set this true. If declared outside a
- /// function (such that it gets deleted during program rundown), set false
- /// (the default).\n
- /// \n
- /// The problems encountered was that during program rundown, one logging
- /// implementation (log4cxx) threw a MutexException (this is described in
- /// https://issues.apache.org/jira/browse/LOGCXX-322). As this only occurs
- /// during program rundown, the issue is not serious - it just looks bad to
- /// have the program crash instead of shut down cleanly.\n
- /// \n
- /// If log4cxx is chosen as the implementation, this flag controls the
- /// deletion of the underlying log4cxx data structures when the logger is
- /// deleted. Setting it false for externally-declared loggers inhibits
- /// their deletion; so at program exit the memory is not reclaimed during
- /// program rundown, only when the process is selected. Setting it true
- /// for loggers that will be deleted in the normal running of the program
- /// enables their deletion - which causes no issues as the problem only
- /// manifests itself during program rundown.
- /// \n
- /// The flag has no effect on non-log4cxx implementations.
- Logger(const std::string& name, bool infunc = false) :
- loggerptr_(NULL), name_(name), infunc_(infunc)
+ Logger(const std::string& name) : loggerptr_(NULL), name_(name)
{}
/// \brief Destructor
@@ -95,7 +109,6 @@ public:
/// \return The full name of the logger (including the root name)
virtual std::string getName();
-
/// \brief Set Severity Level for Logger
///
/// Sets the level at which this logger will log messages. If none is set,
@@ -107,14 +120,12 @@ public:
/// outside these limits is silently coerced to the nearest boundary.
virtual void setSeverity(isc::log::Severity severity, int dbglevel = 1);
-
/// \brief Get Severity Level for Logger
///
/// \return The current logging level of this logger. In most cases though,
/// the effective logging level is what is required.
virtual isc::log::Severity getSeverity();
-
/// \brief Get Effective Severity Level for Logger
///
/// \return The effective severity level of the logger. This is the same
@@ -122,13 +133,18 @@ public:
/// is the severity of the parent.
virtual isc::log::Severity getEffectiveSeverity();
-
/// \brief Return DEBUG Level
///
/// \return Current setting of debug level. This is returned regardless of
/// whether the severity is set to debug.
virtual int getDebugLevel();
+ /// \brief Get Effective Debug Level for Logger
+ ///
+ /// \return The effective debug level of the logger. This is the same
+ /// as getDebugLevel() if the logger has a debug level set, but otherwise
+ /// is the debug level of the parent.
+ virtual int getEffectiveDebugLevel();
/// \brief Returns if Debug Message Should Be Output
///
@@ -137,23 +153,18 @@ public:
/// checked is less than or equal to the debug level set for the logger.
virtual bool isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL);
-
/// \brief Is INFO Enabled?
virtual bool isInfoEnabled();
-
/// \brief Is WARNING Enabled?
virtual bool isWarnEnabled();
-
/// \brief Is ERROR Enabled?
virtual bool isErrorEnabled();
-
/// \brief Is FATAL Enabled?
virtual bool isFatalEnabled();
-
/// \brief Output Debug Message
///
/// \param dbglevel Debug level, ranging between 0 and 99. Higher numbers
@@ -161,25 +172,21 @@ public:
/// \param ident Message identification.
Formatter debug(int dbglevel, const MessageID& ident);
-
/// \brief Output Informational Message
///
/// \param ident Message identification.
Formatter info(const MessageID& ident);
-
/// \brief Output Warning Message
///
/// \param ident Message identification.
Formatter warn(const MessageID& ident);
-
/// \brief Output Error Message
///
/// \param ident Message identification.
Formatter error(const MessageID& ident);
-
/// \brief Output Fatal Message
///
/// \param ident Message identification.
@@ -188,25 +195,20 @@ public:
/// \brief Equality
///
/// Check if two instances of this logger refer to the same stream.
- /// (This method is principally for testing.)
///
/// \return true if the logger objects are instances of the same logger.
bool operator==(Logger& other);
-protected:
-
- /// \brief Reset Global Data
- ///
- /// Used for testing, this calls upon the underlying logger implementation
- /// to clear any global data.
- static void reset();
-
private:
friend class isc::log::Formatter<Logger>;
+
/// \brief Raw output function
///
/// This is used by the formatter to output formatted output.
- void output(const char* sevText, const std::string& message);
+ ///
+ /// \param severity Severity of the message being output.
+ /// \param message Text of the message to be output.
+ void output(const Severity& severity, const std::string& message);
/// \brief Copy Constructor
///
@@ -245,7 +247,6 @@ private:
LoggerImpl* loggerptr_; ///< Pointer to the underlying logger
std::string name_; ///< Copy of the logger name
- bool infunc_; ///< Copy of the infunc argument
};
} // namespace log
diff --git a/src/lib/log/logger_impl.cc b/src/lib/log/logger_impl.cc
index b30f835..cbf2f1f 100644
--- a/src/lib/log/logger_impl.cc
+++ b/src/lib/log/logger_impl.cc
@@ -19,38 +19,37 @@
#include <stdarg.h>
#include <stdio.h>
#include <boost/lexical_cast.hpp>
+#include <boost/static_assert.hpp>
+
+#include <log4cplus/configurator.h>
-#include <log/debug_levels.h>
-#include <log/root_logger_name.h>
#include <log/logger.h>
#include <log/logger_impl.h>
+#include <log/logger_level.h>
+#include <log/logger_level_impl.h>
+#include <log/logger_name.h>
#include <log/message_dictionary.h>
#include <log/message_types.h>
-#include <log/root_logger_name.h>
#include <util/strutil.h>
+// Note: as log4cplus and the BIND 10 logger have many concepts in common, and
+// thus many similar names, to disambiguate types we don't "use" the log4cplus
+// namespace: instead, all log4cplus types are explicitly qualified.
+
using namespace std;
namespace isc {
namespace log {
-// Static initializations
-
-LoggerImpl::LoggerInfoMap LoggerImpl::logger_info_;
-LoggerImpl::LoggerInfo LoggerImpl::root_logger_info_(isc::log::INFO, 0);
-
-// Constructor
-LoggerImpl::LoggerImpl(const std::string& name, bool)
+// Constructor. The setting of logger_ must be done when the variable is
+// constructed (instead of being left to the body of the function); at least
+// one compiler requires that all member variables be constructed before the
+// constructor is run, but log4cplus::Logger (the type of logger_) has no
+// default constructor.
+LoggerImpl::LoggerImpl(const string& name) : name_(expandLoggerName(name)),
+ logger_(log4cplus::Logger::getInstance(name_))
{
- // Are we the root logger?
- if (name == getRootLoggerName()) {
- is_root_ = true;
- name_ = name;
- } else {
- is_root_ = false;
- name_ = getRootLoggerName() + "." + name;
- }
}
// Destructor. (Here because of virtual declaration.)
@@ -59,140 +58,42 @@ LoggerImpl::~LoggerImpl() {
}
// Set the severity for logging.
-
void
LoggerImpl::setSeverity(isc::log::Severity severity, int dbglevel) {
-
- // Silently coerce the debug level into the valid range of 0 to 99
-
- int debug_level = max(MIN_DEBUG_LEVEL, min(MAX_DEBUG_LEVEL, dbglevel));
- if (is_root_) {
-
- // Can only set severity for the root logger, you can't disable it.
- // Any attempt to do so is silently ignored.
- if (severity != isc::log::DEFAULT) {
- root_logger_info_ = LoggerInfo(severity, debug_level);
- }
-
- } else if (severity == isc::log::DEFAULT) {
-
- // Want to set to default; this means removing the information
- // about this logger from the logger_info_ if it is set.
- LoggerInfoMap::iterator i = logger_info_.find(name_);
- if (i != logger_info_.end()) {
- logger_info_.erase(i);
- }
-
- } else {
-
- // Want to set this information
- logger_info_[name_] = LoggerInfo(severity, debug_level);
- }
+ Level level(severity, dbglevel);
+ logger_.setLogLevel(LoggerLevelImpl::convertFromBindLevel(level));
}
// Return severity level
-
isc::log::Severity
LoggerImpl::getSeverity() {
+ Level level = LoggerLevelImpl::convertToBindLevel(logger_.getLogLevel());
+ return level.severity;
+}
- if (is_root_) {
- return (root_logger_info_.severity);
- }
- else {
- LoggerInfoMap::iterator i = logger_info_.find(name_);
- if (i != logger_info_.end()) {
- return ((i->second).severity);
- }
- else {
- return (isc::log::DEFAULT);
- }
- }
+// Return current debug level (only valid if current severity level is DEBUG).
+int
+LoggerImpl::getDebugLevel() {
+ Level level = LoggerLevelImpl::convertToBindLevel(logger_.getLogLevel());
+ return level.dbglevel;
}
// Get effective severity. Either the current severity or, if not set, the
// severity of the root level.
-
isc::log::Severity
LoggerImpl::getEffectiveSeverity() {
-
- if (!is_root_ && !logger_info_.empty()) {
-
- // Not root logger and there is at least one item in the info map for a
- // logger.
- LoggerInfoMap::iterator i = logger_info_.find(name_);
- if (i != logger_info_.end()) {
-
- // Found, so return the severity.
- return ((i->second).severity);
- }
- }
-
- // Must be the root logger, or this logger is defaulting to the root logger
- // settings.
- return (root_logger_info_.severity);
+ Level level = LoggerLevelImpl::convertToBindLevel(logger_.getChainedLogLevel());
+ return level.severity;
}
-// Get the debug level. This returns 0 unless the severity is DEBUG.
-
+// Return effective debug level (only valid if current effective severity level
+// is DEBUG).
int
-LoggerImpl::getDebugLevel() {
-
- if (!is_root_ && !logger_info_.empty()) {
-
- // Not root logger and there is something in the map, check if there
- // is a setting for this one.
- LoggerInfoMap::iterator i = logger_info_.find(name_);
- if (i != logger_info_.end()) {
-
- // Found, so return the debug level.
- if ((i->second).severity == isc::log::DEBUG) {
- return ((i->second).dbglevel);
- } else {
- return (0);
- }
- }
- }
-
- // Must be the root logger, or this logger is defaulting to the root logger
- // settings.
- if (root_logger_info_.severity == isc::log::DEBUG) {
- return (root_logger_info_.dbglevel);
- } else {
- return (0);
- }
+LoggerImpl::getEffectiveDebugLevel() {
+ Level level = LoggerLevelImpl::convertToBindLevel(logger_.getChainedLogLevel());
+ return level.dbglevel;
}
-// The code for isXxxEnabled is quite simple and is in the header. The only
-// exception is isDebugEnabled() where we have the complication of the debug
-// levels.
-
-bool
-LoggerImpl::isDebugEnabled(int dbglevel) {
-
- if (!is_root_ && !logger_info_.empty()) {
-
- // Not root logger and there is something in the map, check if there
- // is a setting for this one.
- LoggerInfoMap::iterator i = logger_info_.find(name_);
- if (i != logger_info_.end()) {
-
- // Found, so return the debug level.
- if ((i->second).severity <= isc::log::DEBUG) {
- return ((i->second).dbglevel >= dbglevel);
- } else {
- return (false); // Nothing lower than debug
- }
- }
- }
-
- // Must be the root logger, or this logger is defaulting to the root logger
- // settings.
- if (root_logger_info_.severity <= isc::log::DEBUG) {
- return (root_logger_info_.dbglevel >= dbglevel);
- } else {
- return (false);
- }
-}
// Output a general message
string*
@@ -202,18 +103,27 @@ LoggerImpl::lookupMessage(const MessageID& ident) {
}
void
-LoggerImpl::outputRaw(const char* sevText, const string& message) {
- // Get the time in a struct tm format, and convert to text
- time_t t_time;
- time(&t_time);
- struct tm* tm_time = localtime(&t_time);
-
- char chr_time[32];
- (void) strftime(chr_time, sizeof(chr_time), "%Y-%m-%d %H:%M:%S", tm_time);
-
- // Now output.
- cout << chr_time << " " << sevText << " [" << getName() << "] " <<
- message << endl;
+LoggerImpl::outputRaw(const Severity& severity, const string& message) {
+ switch (severity) {
+ case DEBUG:
+ LOG4CPLUS_DEBUG(logger_, message);
+ break;
+
+ case INFO:
+ LOG4CPLUS_INFO(logger_, message);
+ break;
+
+ case WARN:
+ LOG4CPLUS_WARN(logger_, message);
+ break;
+
+ case ERROR:
+ LOG4CPLUS_ERROR(logger_, message);
+ break;
+
+ case FATAL:
+ LOG4CPLUS_FATAL(logger_, message);
+ }
}
} // namespace log
diff --git a/src/lib/log/logger_impl.h b/src/lib/log/logger_impl.h
index 187e478..90bd41a 100644
--- a/src/lib/log/logger_impl.h
+++ b/src/lib/log/logger_impl.h
@@ -18,15 +18,19 @@
#include <stdarg.h>
#include <time.h>
+#include <iostream>
#include <cstdlib>
#include <string>
#include <map>
#include <utility>
-#include <log/debug_levels.h>
-#include <log/logger.h>
+
+// log4cplus logger header file
+#include <log4cplus/logger.h>
+
+// BIND-10 logger files
+#include <log/logger_level_impl.h>
#include <log/message_types.h>
-#include <log/root_logger_name.h>
namespace isc {
namespace log {
@@ -35,46 +39,36 @@ namespace log {
///
/// The logger uses a "pimpl" idiom for implementation, where the base logger
/// class contains little more than a pointer to the implementation class, and
-/// all actions are carried out by the latter. This class is an implementation
-/// class that just outputs to stdout.
+/// all actions are carried out by the latter.
+///
+/// This particular implementation is based on log4cplus (from sourceforge:
+/// http://log4cplus.sourceforge.net). Particular items of note:
+///
+/// a) BIND 10 loggers have names of the form "program.sublogger". In other
+/// words, each of the loggers is a sub-logger of the main program logger.
+/// In log4cplus, there is a root logger (called "root" according to the
+/// documentation, but actually unnamed) and all loggers created are subloggers
+/// if it.
+///
+/// In this implementation, the log4cplus root logger is unused. Instead, the
+/// BIND 10 root logger is created as a child of the log4cplus root logger,
+/// and all other loggers used in the program are created as sub-loggers of
+/// that. In this way, the logging system can just include the name of the
+/// logger in each message without the need to specially consider if the
+/// message is the root logger or not.
+///
+/// b) The idea of debug levels is implemented. See logger_level.h and
+/// logger_level_impl.h for more details on this.
class LoggerImpl {
public:
- /// \brief Information About Logger
- ///
- /// Holds a information about a logger, namely its severity and its debug
- /// level. This could be a std::pair, except that it gets confusing when
- /// accessing the LoggerInfoMap: that returns a pair, so we to reference
- /// elements we would use constructs like ((i->first).second);
- struct LoggerInfo {
- isc::log::Severity severity;
- int dbglevel;
-
- LoggerInfo(isc::log::Severity sev = isc::log::INFO,
- int dbg = MIN_DEBUG_LEVEL) : severity(sev), dbglevel(dbg)
- {}
- };
-
-
- /// \brief Information About All Loggers
- ///
- /// Information about all loggers in the system - except the root logger -
- /// is held in a map, linking name of the logger (excluding the root
- /// name component) and its set severity and debug levels. The root
- /// logger information is held separately.
- typedef std::map<std::string, LoggerInfo> LoggerInfoMap;
-
-
/// \brief Constructor
///
/// Creates a logger of the specific name.
///
/// \param name Name of the logger.
- ///
- /// \param exit_delete This argument is present to get round a bug in
- /// the log4cxx implementation. It is unused here.
- LoggerImpl(const std::string& name, bool);
+ LoggerImpl(const std::string& name);
/// \brief Destructor
@@ -94,16 +88,16 @@ public:
///
/// \param severity Severity level to log
/// \param dbglevel If the severity is DEBUG, this is the debug level.
- /// This can be in the range 1 to 100 and controls the verbosity. A value
+ /// This can be in the range 0 to 99 and controls the verbosity. A value
/// outside these limits is silently coerced to the nearest boundary.
- virtual void setSeverity(isc::log::Severity severity, int dbglevel = 1);
+ virtual void setSeverity(Severity severity, int dbglevel = 1);
/// \brief Get Severity Level for Logger
///
/// \return The current logging level of this logger. In most cases though,
/// the effective logging level is what is required.
- virtual isc::log::Severity getSeverity();
+ virtual Severity getSeverity();
/// \brief Get Effective Severity Level for Logger
@@ -111,67 +105,62 @@ public:
/// \return The effective severity level of the logger. This is the same
/// as getSeverity() if the logger has a severity level set, but otherwise
/// is the severity of the parent.
- virtual isc::log::Severity getEffectiveSeverity();
+ virtual Severity getEffectiveSeverity();
- /// \brief Return DEBUG Level
+ /// \brief Return debug level
///
- /// \return Current setting of debug level. This is returned regardless of
- /// whether the
+ /// \return Current setting of debug level. This will be zero if the
+ /// the current severity level is not DEBUG.
virtual int getDebugLevel();
+ /// \brief Return effective debug level
+ ///
+ /// \return Current setting of effective debug level. This will be zero if
+ /// the current effective severity level is not DEBUG.
+ virtual int getEffectiveDebugLevel();
+
+
/// \brief Returns if Debug Message Should Be Output
///
/// \param dbglevel Level for which debugging is checked. Debugging is
/// enabled only if the logger has DEBUG enabled and if the dbglevel
/// checked is less than or equal to the debug level set for the logger.
- virtual bool
- isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL);
+ virtual bool isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL) {
+ Level level(DEBUG, dbglevel);
+ return logger_.isEnabledFor(LoggerLevelImpl::convertFromBindLevel(level));
+ }
/// \brief Is INFO Enabled?
virtual bool isInfoEnabled() {
- return (isEnabled(isc::log::INFO));
+ return (logger_.isEnabledFor(log4cplus::INFO_LOG_LEVEL));
}
/// \brief Is WARNING Enabled?
virtual bool isWarnEnabled() {
- return (isEnabled(isc::log::WARN));
+ return (logger_.isEnabledFor(log4cplus::WARN_LOG_LEVEL));
}
/// \brief Is ERROR Enabled?
virtual bool isErrorEnabled() {
- return (isEnabled(isc::log::ERROR));
+ return (logger_.isEnabledFor(log4cplus::ERROR_LOG_LEVEL));
}
/// \brief Is FATAL Enabled?
virtual bool isFatalEnabled() {
- return (isEnabled(isc::log::FATAL));
- }
-
-
- /// \brief Common Severity check
- ///
- /// Implements the common severity check. As an optimisation, this checks
- /// to see if any logger-specific levels have been set (a quick check as it
- /// just involves seeing if the collection of logger information is empty).
- /// if not, it returns the information for the root level; if so, it has
- /// to take longer and look up the information in the map holding the
- /// logging details.
- virtual bool isEnabled(isc::log::Severity severity) {
- if (logger_info_.empty()) {
- return (root_logger_info_.severity <= severity);
- }
- else {
- return (getSeverity() <= severity);
- }
+ return (logger_.isEnabledFor(log4cplus::FATAL_LOG_LEVEL));
}
/// \brief Raw output
///
/// Writes the message with time into the log. Used by the Formatter
/// to produce output.
- void outputRaw(const char* sev_text, const std::string& message);
+ ///
+ /// \param severity Severity of the message. (This controls the prefix
+ /// label output with the message text.)
+ /// \param message Text of the message.
+ void outputRaw(const Severity& severity, const std::string& message);
/// \brief Look up message text in dictionary
///
@@ -188,28 +177,9 @@ public:
return (name_ == other.name_);
}
-
- /// \brief Reset Global Data
- ///
- /// Only used for testing, this clears all the logger information and
- /// resets it back to default values.
- static void reset() {
- root_logger_info_ = LoggerInfo(isc::log::INFO, MIN_DEBUG_LEVEL);
- logger_info_.clear();
- }
-
-
private:
- bool is_root_; ///< true if a root logger
- std::string name_; ///< Name of this logger
-
- // Split the status of the root logger from this logger. If - is will
- // probably be the usual case - no per-logger setting is enabled, a
- // quick check of logger_info_.empty() will return true and we can quickly
- // return the root logger status without a length lookup in the map.
-
- static LoggerInfo root_logger_info_; ///< Status of root logger
- static LoggerInfoMap logger_info_; ///< Store of debug levels etc.
+ std::string name_; ///< Full name of this logger
+ log4cplus::Logger logger_; ///< Underlying log4cplus logger
};
} // namespace log
diff --git a/src/lib/log/logger_impl_log4cxx.cc b/src/lib/log/logger_impl_log4cxx.cc
deleted file mode 100644
index afe2d56..0000000
--- a/src/lib/log/logger_impl_log4cxx.cc
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE
-
-#include <iostream>
-
-#include <stdarg.h>
-#include <stdio.h>
-
-#include <log4cxx/appender.h>
-#include <log4cxx/basicconfigurator.h>
-#include <log4cxx/patternlayout.h>
-#include <log4cxx/consoleappender.h>
-
-#include <log/root_logger_name.h>
-#include <log/logger.h>
-#include <log/logger_impl.h>
-#include <log/message_dictionary.h>
-#include <log/message_types.h>
-#include <log/xdebuglevel.h>
-
-#include <util/strutil.h>
-
-using namespace std;
-
-namespace isc {
-namespace log {
-
-// Static initializations
-
-bool LoggerImpl::init_ = false;
-
-// Destructor. Delete log4cxx stuff if "don't delete" is clear.
-
-LoggerImpl::~LoggerImpl() {
- if (exit_delete_) {
- delete loggerptr_;
- }
-}
-
-// Initialize logger - create a logger as a child of the root logger. With
-// log4cxx this is assured by naming the logger <parent>.<child>.
-
-void
-LoggerImpl::initLogger() {
-
- // Initialize basic logging if not already done. This is a one-off for
- // all loggers.
- if (!init_) {
-
- // TEMPORARY
- // Add a suitable console logger to the log4cxx root logger. (This
- // is the logger at the root of the log4cxx tree, not the BIND-10 root
- // logger, which is one level down.) The chosen format is:
- //
- // YYYY-MM-DD hh:mm:ss.sss [logger] SEVERITY: text
- //
- // As noted, this is a temporary hack: it is done here to ensure that
- // a suitable output and output pattern is set. Future versions of the
- // software will set this based on configuration data.
-
- log4cxx::LayoutPtr layout(
- new log4cxx::PatternLayout(
- "%d{yyyy-MM-DD HH:mm:ss.SSS} %-5p [%c] %m\n"));
- log4cxx::AppenderPtr console(
- new log4cxx::ConsoleAppender(layout));
- log4cxx::LoggerPtr sys_root_logger = log4cxx::Logger::getRootLogger();
- sys_root_logger->addAppender(console);
-
- // Set the default logging to INFO
- sys_root_logger->setLevel(log4cxx::Level::getInfo());
-
- // All static stuff initialized
- init_ = true;
- }
-
- // Initialize this logger. Name this as to whether the BIND-10 root logger
- // name has been set. (If not, this mucks up the hierarchy :-( ).
- string root_name = RootLoggerName::getName();
- if (root_name.empty() || (name_ == root_name)) {
- loggerptr_ = new log4cxx::LoggerPtr(log4cxx::Logger::getLogger(name_));
- }
- else {
- loggerptr_ = new log4cxx::LoggerPtr(
- log4cxx::Logger::getLogger(root_name + "." + name_)
- );
- }
-}
-
-
-// Set the severity for logging. There is a 1:1 mapping between the logging
-// severity and the log4cxx logging levels, apart from DEBUG.
-//
-// In log4cxx, each of the logging levels (DEBUG, INFO, WARN etc.) has a numeric
-// value. The level is set to one of these and any numeric level equal to or
-// above it that is reported. For example INFO has a value of 20000 and ERROR
-// a value of 40000. So if a message of WARN severity (= 30000) is logged, it is
-// not logged when the logger's severity level is ERROR (as 30000 !>= 40000).
-// It is reported if the logger's severity level is set to WARN (as 30000 >=
-/// 30000) or INFO (30000 >= 20000).
-//
-// This gives a simple system for handling different debug levels. The debug
-// level is a number between 0 and 99, with 0 being least verbose and 99 the
-// most. To implement this seamlessly, when DEBUG is set, the numeric value
-// of the logging level is actually set to (DEBUG - debug-level). Similarly
-// messages of level "n" are logged at a logging level of (DEBUG - n). Thus if
-// the logging level is set to DEBUG and the debug level set to 25, the actual
-// level set is 10000 - 25 = 99975.
-//
-// Attempting to log a debug message of level 26 is an attempt to log a message
-// of level 10000 - 26 = 9974. As 9974 !>= 9975, it is not logged. A
-// message of level 25 is, because 9975 >= 9975.
-//
-// The extended set of logging levels is implemented by the XDebugLevel class.
-
-void
-LoggerImpl::setSeverity(isc::log::Severity severity, int dbglevel) {
- switch (severity) {
- case NONE:
- getLogger()->setLevel(log4cxx::Level::getOff());
- break;
-
- case FATAL:
- getLogger()->setLevel(log4cxx::Level::getFatal());
- break;
-
- case ERROR:
- getLogger()->setLevel(log4cxx::Level::getError());
- break;
-
- case WARN:
- getLogger()->setLevel(log4cxx::Level::getWarn());
- break;
-
- case INFO:
- getLogger()->setLevel(log4cxx::Level::getInfo());
- break;
-
- case DEBUG:
- getLogger()->setLevel(
- log4cxx::XDebugLevel::getExtendedDebug(dbglevel));
- break;
-
- // Will get here for DEFAULT or any other value. This disables the
- // logger's own severity and it defaults to the severity of the parent
- // logger.
- default:
- getLogger()->setLevel(0);
- }
-}
-
-// Convert between numeric log4cxx logging level and BIND-10 logging severity.
-
-isc::log::Severity
-LoggerImpl::convertLevel(int value) {
-
- // The order is optimised. This is only likely to be called when testing
- // for writing debug messages, so the check for DEBUG_INT is first.
- if (value <= log4cxx::Level::DEBUG_INT) {
- return (DEBUG);
- } else if (value <= log4cxx::Level::INFO_INT) {
- return (INFO);
- } else if (value <= log4cxx::Level::WARN_INT) {
- return (WARN);
- } else if (value <= log4cxx::Level::ERROR_INT) {
- return (ERROR);
- } else if (value <= log4cxx::Level::FATAL_INT) {
- return (FATAL);
- } else {
- return (NONE);
- }
-}
-
-
-// Return the logging severity associated with this logger.
-
-isc::log::Severity
-LoggerImpl::getSeverityCommon(const log4cxx::LoggerPtr& ptrlogger,
- bool check_parent) {
-
- log4cxx::LevelPtr level = ptrlogger->getLevel();
- if (level == log4cxx::LevelPtr()) {
-
- // Null level returned, logging should be that of the parent.
-
- if (check_parent) {
- log4cxx::LoggerPtr parent = ptrlogger->getParent();
- if (parent == log4cxx::LoggerPtr()) {
-
- // No parent, so reached the end of the chain. Return INFO
- // severity.
- return (INFO);
- }
- else {
- return (getSeverityCommon(parent, check_parent));
- }
- }
- else {
- return (DEFAULT);
- }
- } else {
- return (convertLevel(level->toInt()));
- }
-}
-
-
-// Get the debug level. This returns 0 unless the severity is DEBUG.
-
-int
-LoggerImpl::getDebugLevel() {
-
- log4cxx::LevelPtr level = getLogger()->getLevel();
- if (level == log4cxx::LevelPtr()) {
-
- // Null pointer returned, logging should be that of the parent.
- return (0);
-
- } else {
- int severity = level->toInt();
- if (severity <= log4cxx::Level::DEBUG_INT) {
- return (log4cxx::Level::DEBUG_INT - severity);
- }
- else {
- return (0);
- }
- }
-}
-
-
-
-} // namespace log
-} // namespace isc
diff --git a/src/lib/log/logger_impl_log4cxx.h b/src/lib/log/logger_impl_log4cxx.h
deleted file mode 100644
index 3101347..0000000
--- a/src/lib/log/logger_impl_log4cxx.h
+++ /dev/null
@@ -1,315 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __LOGGER_IMPL_LOG4CXX_H
-#define __LOGGER_IMPL_LOG4CXX_H
-
-#include <cstdlib>
-#include <string>
-#include <boost/lexical_cast.hpp>
-#include <log4cxx/logger.h>
-#include <log4cxx/logger.h>
-
-#include <log/debug_levels.h>
-#include <log/logger.h>
-#include <log/message_types.h>
-
-namespace isc {
-namespace log {
-
-/// \brief Log4cxx Logger Implementation
-///
-/// The logger uses a "pimpl" idiom for implementation, where the base logger
-/// class contains little more than a pointer to the implementation class, and
-/// all actions are carried out by the latter. This class is an implementation
-/// class interfacing to the log4cxx logging system.
-
-class LoggerImpl {
-public:
-
- /// \brief Constructor
- ///
- /// Creates/attaches to a logger of a specific name.
- ///
- /// \param name Name of the logger. If the name is that of the root name,
- /// this creates an instance of the root logger; otherwise it creates a
- /// child of the root logger.
- ///
- /// \param exit_delete This argument is present to get round a bug in
- /// log4cxx. If a log4cxx logger is declared outside an execution unit, it
- /// is not deleted until the program runs down. At that point all such
- /// objects - including internal log4cxx objects - are deleted. However,
- /// there seems to be a bug in log4cxx where the way that such objects are
- /// destroyed causes a MutexException to be thrown (this is described in
- /// https://issues.apache.org/jira/browse/LOGCXX-322). As this only occurs
- /// during program rundown, the issue is not serious - it just looks bad to
- /// have the program crash instead of shut down cleanly.\n
- /// \n
- /// The original implementation of the isc::log::Logger had as a member a
- /// log4cxx logger (actually a LoggerPtr). If the isc:: Logger was declared
- /// statically, when it was destroyed at the end of the program the internal
- /// LoggerPtr was destroyed, which triggered the problem. The problem did
- /// not occur if the isc::log::Logger was created on the stack. To get
- /// round this, the internal LoggerPtr is now created dynamically. The
- /// exit_delete argument controls its destruction: if true, it is destroyed
- /// in the ISC Logger destructor. If false, it is not.\n
- /// \n
- /// When creating an isc::log::Logger on the stack, the argument should be
- /// false (the default); when the Logger is destroyed, all the internal
- /// log4cxx objects are destroyed. As only the logger (and not the internal
- /// log4cxx data structures are being destroyed), all is well. However,
- /// when creating the logger statically, the argument should be false. This
- /// means that the log4cxx objects are not destroyed at program rundown;
- /// instead memory is reclaimed and files are closed when the process is
- /// destroyed, something that does not trigger the bug.
- LoggerImpl(const std::string& name, bool exit_delete = false) :
- loggerptr_(NULL), name_(name), exit_delete_(exit_delete)
- {}
-
-
- /// \brief Destructor
- virtual ~LoggerImpl();
-
-
- /// \brief Get the full name of the logger (including the root name)
- virtual std::string getName() {
- return (getLogger()->getName());
- }
-
-
- /// \brief Set Severity Level for Logger
- ///
- /// Sets the level at which this logger will log messages. If none is set,
- /// the level is inherited from the parent.
- ///
- /// \param severity Severity level to log
- /// \param dbglevel If the severity is DEBUG, this is the debug level.
- /// This can be in the range 1 to 100 and controls the verbosity. A value
- /// outside these limits is silently coerced to the nearest boundary.
- virtual void setSeverity(isc::log::Severity severity, int dbglevel = 1);
-
-
- /// \brief Get Severity Level for Logger
- ///
- /// \return The current logging level of this logger. In most cases though,
- /// the effective logging level is what is required.
- virtual isc::log::Severity getSeverity() {
- return (getSeverityCommon(getLogger(), false));
- }
-
-
- /// \brief Get Effective Severity Level for Logger
- ///
- /// \return The effective severity level of the logger. This is the same
- /// as getSeverity() if the logger has a severity level set, but otherwise
- /// is the severity of the parent.
- virtual isc::log::Severity getEffectiveSeverity() {
- return (getSeverityCommon(getLogger(), true));
- }
-
-
- /// \brief Return DEBUG Level
- ///
- /// \return Current setting of debug level. This is returned regardless of
- /// whether the
- virtual int getDebugLevel();
-
-
- /// \brief Returns if Debug Message Should Be Output
- ///
- /// \param dbglevel Level for which debugging is checked. Debugging is
- /// enabled only if the logger has DEBUG enabled and if the dbglevel
- /// checked is less than or equal to the debug level set for the logger.
- virtual bool
- isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL) {
- return (getLogger()->getEffectiveLevel()->toInt() <=
- (log4cxx::Level::DEBUG_INT - dbglevel));
- }
-
-
- /// \brief Is INFO Enabled?
- virtual bool isInfoEnabled() {
- return (getLogger()->isInfoEnabled());
- }
-
-
- /// \brief Is WARNING Enabled?
- virtual bool isWarnEnabled() {
- return (getLogger()->isWarnEnabled());
- }
-
-
- /// \brief Is ERROR Enabled?
- virtual bool isErrorEnabled() {
- return (getLogger()->isErrorEnabled());
- }
-
-
- /// \brief Is FATAL Enabled?
- virtual bool isFatalEnabled() {
- return (getLogger()->isFatalEnabled());
- }
-
-
- /// \brief Output Debug Message
- ///
- /// \param ident Message identification.
- /// \param text Text to log
- void debug(const MessageID& ident, const char* text) {
- LOG4CXX_DEBUG(getLogger(), ident << ", " << text);
- }
-
-
- /// \brief Output Informational Message
- ///
- /// \param ident Message identification.
- /// \param text Text to log
- void info(const MessageID& ident, const char* text) {
- LOG4CXX_INFO(getLogger(), ident << ", " << text);
- }
-
-
- /// \brief Output Warning Message
- ///
- /// \param ident Message identification.
- /// \param text Text to log
- void warn(const MessageID& ident, const char* text) {
- LOG4CXX_WARN(getLogger(), ident << ", " << text);
- }
-
-
- /// \brief Output Error Message
- ///
- /// \param ident Message identification.
- /// \param text Text to log
- void error(const MessageID& ident, const char* text) {
- LOG4CXX_ERROR(getLogger(), ident << ", " << text);
- }
-
-
- /// \brief Output Fatal Message
- ///
- /// \param ident Message identification.
- /// \param text Text to log
- void fatal(const MessageID& ident, const char* text) {
- LOG4CXX_FATAL(getLogger(), ident << ", " << text);
- }
-
- //@{
- /// \brief Testing Methods
- ///
- /// The next set of methods are used in testing. As they are accessed from
- /// the main logger class, they must be public.
-
- /// \brief Equality
- ///
- /// Check if two instances of this logger refer to the same stream.
- /// (This method is principally for testing.)
- ///
- /// \return true if the logger objects are instances of the same logger.
- bool operator==(LoggerImpl& other) {
- return (*loggerptr_ == *other.loggerptr_);
- }
-
-
- /// \brief Logger Initialized
- ///
- /// Check that the logger has been properly initialized. (This method
- /// is principally for testing.)
- ///
- /// \return true if this logger object has been initialized.
- bool isInitialized() {
- return (loggerptr_ != NULL);
- }
-
- /// \brief Reset Global Data
- ///
- /// Only used for testing, this clears all the logger information and
- /// resets it back to default values. This is a no-op for log4cxx.
- static void reset() {
- }
-
- //@}
-
-protected:
-
- /// \brief Convert Between BIND-10 and log4cxx Logging Levels
- ///
- /// This method is marked protected to allow for unit testing.
- ///
- /// \param value log4cxx numeric logging level
- ///
- /// \return BIND-10 logging severity
- isc::log::Severity convertLevel(int value);
-
-private:
-
- /// \brief Get Severity Level for Logger
- ///
- /// This is common code for getSeverity() and getEffectiveSeverity() -
- /// it returns the severity of the logger; if not set (and the check_parent)
- /// flag is set, it searches up the parent-child tree until a severity
- /// level is found and uses that.
- ///
- /// \param ptrlogger Pointer to the log4cxx logger to check.
- /// \param check_parent true to search up the tree, false to return the
- /// current level.
- ///
- /// \return The effective severity level of the logger. This is the same
- /// as getSeverity() if the logger has a severity level set, but otherwise
- /// is the severity of the parent.
- isc::log::Severity getSeverityCommon(const log4cxx::LoggerPtr& ptrlogger,
- bool check_parent);
-
-
-
- /// \brief Initialize log4cxx Logger
- ///
- /// Creates the log4cxx logger used internally. A function is provided for
- /// this so that the creation does not take place when this Logger object
- /// is created but when it is used. As the latter occurs in executable
- /// code but the former can occur during initialization, this order
- /// guarantees that anything that is statically initialized has completed
- /// its initialization by the time the logger is used.
- void initLogger();
-
-
- /// \brief Return underlying log4cxx logger, initializing it if necessary
- ///
- /// \return Loggerptr object
- log4cxx::LoggerPtr& getLogger() {
- if (loggerptr_ == NULL) {
- initLogger();
- }
- return (*loggerptr_);
- }
-
- // Members. Note that loggerptr_ is a pointer to a LoggerPtr, which is
- // itself a pointer to the underlying log4cxx logger. This is due to the
- // problems with memory deletion on program exit, explained in the comments
- // for the "exit_delete" parameter in this class's constructor.
-
- log4cxx::LoggerPtr* loggerptr_; ///< Pointer to the underlying logger
- std::string name_; ///< Name of this logger]
- bool exit_delete_; ///< Delete loggerptr_ on exit?
-
- // NOTE - THIS IS A PLACE HOLDER
- static bool init_; ///< Set true when initialized
-};
-
-} // namespace log
-} // namespace isc
-
-
-#endif // __LOGGER_IMPL_LOG4CXX_H
diff --git a/src/lib/log/logger_level.cc b/src/lib/log/logger_level.cc
new file mode 100644
index 0000000..5f74eb3
--- /dev/null
+++ b/src/lib/log/logger_level.cc
@@ -0,0 +1,48 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE
+
+#include <log/logger_level.h>
+#include <log/macros.h>
+#include <log/messagedef.h>
+
+#include <boost/algorithm/string.hpp>
+
+
+namespace isc {
+namespace log {
+
+isc::log::Severity
+getSeverity(const std::string& sev_str) {
+ if (boost::iequals(sev_str, "DEBUG")) {
+ return isc::log::DEBUG;
+ } else if (boost::iequals(sev_str, "INFO")) {
+ return isc::log::INFO;
+ } else if (boost::iequals(sev_str, "WARN")) {
+ return isc::log::WARN;
+ } else if (boost::iequals(sev_str, "ERROR")) {
+ return isc::log::ERROR;
+ } else if (boost::iequals(sev_str, "FATAL")) {
+ return isc::log::FATAL;
+ } else if (boost::iequals(sev_str, "NONE")) {
+ return isc::log::NONE;
+ } else {
+ Logger logger("log");
+ LOG_ERROR(logger, MSG_BADSEVERITY).arg(sev_str);
+ return isc::log::INFO;
+ }
+}
+
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_level.h b/src/lib/log/logger_level.h
new file mode 100644
index 0000000..ea60c3c
--- /dev/null
+++ b/src/lib/log/logger_level.h
@@ -0,0 +1,76 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_LEVEL_H
+#define __LOGGER_LEVEL_H
+
+#include <string>
+
+namespace isc {
+namespace log {
+
+/// \brief Severity Levels
+///
+/// Defines the severity levels for logging. This is shared between the logger
+/// and the implementations classes.
+///
+/// N.B. The order of the levels - DEBUG less than INFO less that WARN etc. is
+/// implicitly assumed in several implementations. They must not be changed.
+
+typedef enum {
+ DEFAULT = 0, // Default to logging level of the parent
+ DEBUG = 1,
+ INFO = 2,
+ WARN = 3,
+ ERROR = 4,
+ FATAL = 5,
+ NONE = 6 // Disable logging
+} Severity;
+
+/// Minimum/maximum debug levels.
+
+const int MIN_DEBUG_LEVEL = 0;
+const int MAX_DEBUG_LEVEL = 99;
+
+/// \brief Log level structure
+///
+/// A simple pair structure that provides suitable names for the members. It
+/// holds a combination of logging severity and debug level.
+struct Level {
+ Severity severity; ///< Logging severity
+ int dbglevel; ///< Debug level
+
+ Level(Severity sev = DEFAULT, int dbg = MIN_DEBUG_LEVEL) :
+ severity(sev), dbglevel(dbg)
+ {}
+
+ // Default assignment and copy constructor is appropriate
+};
+
+/// \brief Returns the isc::log::Severity value represented by the given string
+///
+/// This must be one of the strings "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or
+/// "NONE". (Case is not important, but the string most not contain leading or
+/// trailing spaces.)
+///
+/// \param sev_str The string representing severity value
+///
+/// \return The severity. If the string is not recognized, an error will be
+/// logged and the string will return isc::log::INFO.
+isc::log::Severity getSeverity(const std::string& sev_str);
+
+} // namespace log
+} // namespace isc
+
+#endif // __LOGGER_LEVEL_H
diff --git a/src/lib/log/logger_level_impl.cc b/src/lib/log/logger_level_impl.cc
new file mode 100644
index 0000000..d6d8ed7
--- /dev/null
+++ b/src/lib/log/logger_level_impl.cc
@@ -0,0 +1,217 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <algorithm>
+#include <string.h>
+#include <iostream>
+#include <boost/lexical_cast.hpp>
+
+#include <log4cplus/logger.h>
+
+#include <log/impldef.h>
+#include <log/logger_level.h>
+#include <log/logger_level_impl.h>
+#include <log/macros.h>
+
+using namespace log4cplus;
+using namespace std;
+
+namespace {
+isc::log::Logger logger("log");
+}
+
+namespace isc {
+namespace log {
+
+// Convert BIND 10 level to a log4cplus logging level.
+log4cplus::LogLevel
+LoggerLevelImpl::convertFromBindLevel(const Level& level) {
+
+ // BIND 10 logging levels are small integers so we can do a table lookup
+ static const log4cplus::LogLevel log4cplus_levels[] = {
+ log4cplus::NOT_SET_LOG_LEVEL,
+ log4cplus::DEBUG_LOG_LEVEL,
+ log4cplus::INFO_LOG_LEVEL,
+ log4cplus::WARN_LOG_LEVEL,
+ log4cplus::ERROR_LOG_LEVEL,
+ log4cplus::FATAL_LOG_LEVEL,
+ log4cplus::OFF_LOG_LEVEL
+ };
+
+ // ... with compile-time checks to ensure that table indexes are correct.
+ BOOST_STATIC_ASSERT(static_cast<int>(DEFAULT) == 0);
+ BOOST_STATIC_ASSERT(static_cast<int>(DEBUG) == 1);
+ BOOST_STATIC_ASSERT(static_cast<int>(INFO) == 2);
+ BOOST_STATIC_ASSERT(static_cast<int>(WARN) == 3);
+ BOOST_STATIC_ASSERT(static_cast<int>(ERROR) == 4);
+ BOOST_STATIC_ASSERT(static_cast<int>(FATAL) == 5);
+ BOOST_STATIC_ASSERT(static_cast<int>(NONE) == 6);
+
+ // Do the conversion
+ if (level.severity == DEBUG) {
+
+ // Debug severity, so the log4cplus level returned depends on the
+ // debug level. Silently limit the debug level to the range
+ // MIN_DEBUG_LEVEL to MAX_DEBUG_LEVEL (avoids the hassle of throwing
+ // and catching exceptions and besides, this is for debug information).
+ int limited = std::max(MIN_DEBUG_LEVEL,
+ std::min(level.dbglevel, MAX_DEBUG_LEVEL));
+ LogLevel newlevel = static_cast<int>(DEBUG_LOG_LEVEL -
+ (limited - MIN_DEBUG_LEVEL));
+ return (static_cast<log4cplus::LogLevel>(newlevel));
+
+ } else {
+
+ // Can do a table lookup to speed things up. There is no need to check
+ // that the index is out of range. That the variable is of type
+ // isc::log::Severity ensures that it must be one of the Severity enum
+ // members - conversion of a numeric value to an enum is not permitted.
+ return (log4cplus_levels[level.severity]);
+ }
+}
+
+// Convert log4cplus logging level to BIND 10 debug level. It is up to the
+// caller to validate that the debug level is valid.
+Level
+LoggerLevelImpl::convertToBindLevel(const log4cplus::LogLevel loglevel) {
+
+ // Not easy to do a table lookup as the numerical values of log4cplus levels
+ // are quite high.
+ if (loglevel <= log4cplus::NOT_SET_LOG_LEVEL) {
+ return (Level(DEFAULT));
+
+ } else if (loglevel <= log4cplus::DEBUG_LOG_LEVEL) {
+
+ // Debug severity, so extract the debug level from the numeric value.
+ // If outside the limits, change the severity to the level above or
+ // below.
+ int dbglevel = MIN_DEBUG_LEVEL +
+ static_cast<int>(log4cplus::DEBUG_LOG_LEVEL) -
+ static_cast<int>(loglevel);
+ if (dbglevel > MAX_DEBUG_LEVEL) {
+ return (Level(DEFAULT));
+ } else if (dbglevel < MIN_DEBUG_LEVEL) {
+ return (Level(INFO));
+ }
+ return (Level(DEBUG, dbglevel));
+
+ } else if (loglevel <= log4cplus::INFO_LOG_LEVEL) {
+ return (Level(INFO));
+
+ } else if (loglevel <= log4cplus::WARN_LOG_LEVEL) {
+ return (Level(WARN));
+
+ } else if (loglevel <= log4cplus::ERROR_LOG_LEVEL) {
+ return (Level(ERROR));
+
+ } else if (loglevel <= log4cplus::FATAL_LOG_LEVEL) {
+ return (Level(FATAL));
+
+ }
+
+ return (Level(NONE));
+}
+
+
+// Convert string to appropriate logging level
+log4cplus::LogLevel
+LoggerLevelImpl::logLevelFromString(const log4cplus::tstring& level) {
+
+ std::string name = level; // Get to known type
+ size_t length = name.size(); // Length of the string
+
+ if (length < 5) {
+
+ // String can't possibly start DEBUG so we don't know what it is.
+ // As per documentation, return NOT_SET level.
+ return (NOT_SET_LOG_LEVEL);
+ }
+ else {
+ if (strncasecmp(name.c_str(), "DEBUG", 5) == 0) {
+
+ // String starts "DEBUG" (or "debug" or any case mixture). The
+ // rest of the string - if any - should be a number.
+ if (length == 5) {
+
+ // It is plain "DEBUG". Take this as level 0.
+ return (DEBUG_LOG_LEVEL);
+ }
+ else {
+
+ // Try converting the remainder to an integer. The "5" is
+ // the length of the string "DEBUG". Note that if the number
+ // is outside the range of debug levels, it is coerced to the
+ // nearest limit. Thus a level of DEBUG509 will end up as
+ // if DEBUG99 has been specified.
+ try {
+ int dbglevel = boost::lexical_cast<int>(name.substr(5));
+ if (dbglevel < MIN_DEBUG_LEVEL) {
+ LOG_WARN(logger, LOGIMPL_BELOWDBGMIN).arg(dbglevel)
+ .arg(MIN_DEBUG_LEVEL);
+ dbglevel = MIN_DEBUG_LEVEL;
+
+ } else if (dbglevel > MAX_DEBUG_LEVEL) {
+ LOG_WARN(logger, LOGIMPL_ABOVEDBGMAX).arg(dbglevel)
+ .arg(MAX_DEBUG_LEVEL);
+ dbglevel = MAX_DEBUG_LEVEL;
+
+ }
+ return convertFromBindLevel(Level(DEBUG, dbglevel));
+ }
+ catch (boost::bad_lexical_cast&) {
+ LOG_ERROR(logger, LOGIMPL_BADDEBUG).arg(name);
+ return (NOT_SET_LOG_LEVEL);
+ }
+ }
+ } else {
+
+ // Unknown string - return default. Log4cplus will call any other
+ // registered conversion functions to interpret it.
+ return (NOT_SET_LOG_LEVEL);
+ }
+ }
+}
+
+// Convert logging level to string. If the level is a valid debug level,
+// return the string DEBUG, else return the empty string.
+log4cplus::tstring
+LoggerLevelImpl::logLevelToString(log4cplus::LogLevel level) {
+ Level bindlevel = convertToBindLevel(level);
+ Severity& severity = bindlevel.severity;
+ int& dbglevel = bindlevel.dbglevel;
+
+ if ((severity == DEBUG) &&
+ ((dbglevel >= MIN_DEBUG_LEVEL) && (dbglevel <= MAX_DEBUG_LEVEL))) {
+ return (tstring("DEBUG"));
+ }
+
+ // Unknown, so return empty string for log4cplus to try other conversion
+ // functions.
+ return (tstring());
+}
+
+// Initialization. Register the conversion functions with the LogLevelManager.
+void
+LoggerLevelImpl::init() {
+
+ // Get the singleton log-level manager.
+ LogLevelManager& manager = getLogLevelManager();
+
+ // Register the conversion functions
+ manager.pushFromStringMethod(LoggerLevelImpl::logLevelFromString);
+ manager.pushToStringMethod(LoggerLevelImpl::logLevelToString);
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_level_impl.h b/src/lib/log/logger_level_impl.h
new file mode 100644
index 0000000..9289a1d
--- /dev/null
+++ b/src/lib/log/logger_level_impl.h
@@ -0,0 +1,127 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_LEVEL_IMPL_H
+#define __LOGGER_LEVEL_IMPL_H
+
+#include <log4cplus/logger.h>
+#include <log/logger_level.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Implementation aspects of logging levels
+///
+/// This extends the log4cplus level set to allow 100 debug levels.
+///
+/// First some terminology, as the use of the term "level" gets confusing. The
+/// code and comments here use the term "level" in two contexts:
+///
+/// Logging level: The category of messages to log. By default log4cplus
+/// defines the following logging levels: OFF_LOG_LEVEL, FATAL_LOG_LEVEL,
+/// ERROR_LOG_LEVEL, WARN_LOG_LEVEL, INFO_LOG_LEVEL, DEBUG_LOG_LEVEL,
+/// TRACE_LOG_LEVEL, ALL_LOG_LEVEL (which here will be abbreviated OFF, FATAL
+/// etc.). Within the context of BIND-10, OFF, TRACE and ALL are not used
+/// and the idea of DEBUG has been extended, as will be seen below. In
+/// BIND 10 terms, this is known as "severity"; the "logging level" usage will
+/// usually be used when talking about log4cplus aspects of the idea (as
+/// log4cplus uses that teminology).
+///
+/// Debug level: This is a number that ranges from 0 to 99 and is used by the
+/// application to control the detail of debug output. A value of 0 gives the
+/// highest-level debug output; a value of 99 gives the most verbose and most
+/// detailed. Debug messages (or whatever debug level) are only ever output
+/// when the logging level is set to DEBUG. (Note that the numbers 0 and 99
+/// are not hard-coded - they are the constants MIN_DEBUG_LEVEL and
+/// MAX_DEBUG_LEVEL.)
+///
+/// With log4cplus, the various logging levels have a numeric value associated
+/// with them, such that FATAL > ERROR > WARNING etc. This suggests that the
+/// idea of debug levels can be incorporated into the existing logging level
+/// scheme by assigning them appropriate numeric values, i.e.
+///
+/// WARNING > INFO > DEBUG > DEBUG - 1 > DEBUG - 2 > ... > DEBUG - 99
+///
+/// Setting a numeric level of DEBUG enables the basic messages; setting higher
+/// debug levels (corresponding to lower numeric logging levels) will enable
+/// progressively more messages. The lowest debug level (0) is chosen such that
+/// it corresponds to the default level of DEBUG.
+///
+/// This class comprises nothing more than static methods to aid the conversion
+/// of logging levels between log4cplus and BIND 10, and to register those
+/// levels with log4cplus.
+
+class LoggerLevelImpl {
+public:
+
+ /// \brief Convert BIND 10 level to log4cplus logging level
+ ///
+ /// Converts the BIND 10 severity level into a log4cplus logging level.
+ /// If the severity is DEBUG, the current BIND 10 debug level is taken
+ /// into account when doing the conversion.
+ ///
+ /// \param level BIND 10 severity and debug level
+ ///
+ /// \return Equivalent log4cplus logging level.
+ static
+ log4cplus::LogLevel convertFromBindLevel(const isc::log::Level& level);
+
+ /// \brief Convert log4cplus logging level to BIND 10 logging level
+ ///
+ /// Converts the log4cplus log level into a BIND 10 severity level.
+ /// The log4cplus log level may be non-standard in which case it is
+ /// encoding a BIND 10 debug level as well.
+ ///
+ /// \param level log4cplus log level
+ ///
+ /// \return Equivalent BIND 10 severity and debug level
+ static
+ isc::log::Level convertToBindLevel(const log4cplus::LogLevel loglevel);
+
+ /// \brief Convert string to log4cplus logging level
+ ///
+ /// BIND 10 extends the set of logging levels in log4cplus with a group
+ /// of debug levels. These are given names DEBUG0 through DEBUG99 (with
+ /// DEBUG0 being equivalent to DEBUG, the standard log level. If the name
+ /// is DEBUGn but n lies outside the range of debug levels, debug level
+ /// specifies is coerced to the nearest valid value. If the string is just
+ /// not recognised, a NOT_SET_LOG_LEVEL is returned.
+ ///
+ /// \param level String representing the logging level.
+ ///
+ /// \return Corresponding log4cplus log level
+ static
+ log4cplus::LogLevel logLevelFromString(const log4cplus::tstring& level);
+
+ /// \brief Convert log level to string
+ ///
+ /// If the log level is one of the extended debug levels, the string DEBUG
+ /// is returned, otherwise the empty string.
+ ///
+ /// \param level Extended logging level
+ ///
+ /// \return Equivalent string.
+ static log4cplus::tstring logLevelToString(log4cplus::LogLevel level);
+
+ /// \brief Initialize extended logging levels
+ ///
+ /// This must be called once, after log4cplus has been initialized. It
+ /// registers the level/string converter functions.
+ static void init();
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __LOGGER_LEVEL_IMPL_H
diff --git a/src/lib/log/logger_levels.h b/src/lib/log/logger_levels.h
deleted file mode 100644
index 2f123e8..0000000
--- a/src/lib/log/logger_levels.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __LOGGER_LEVELS_H
-#define __LOGGER_LEVELS_H
-
-namespace isc {
-namespace log {
-
-/// \brief Severity Levels
-///
-/// Defines the severity levels for logging. This is shared between the logger
-/// and the implementations classes.
-///
-/// N.B. The order of the levels - DEBUG less than INFO less that WARN etc. is
-/// implicitly assumed in several implementations. They must not be changed.
-
-typedef enum {
- DEFAULT = 0, // Default to logging level of the parent
- DEBUG = 1,
- INFO = 2,
- WARN = 3,
- ERROR = 4,
- FATAL = 5,
- NONE = 6 // Disable logging
-} Severity;
-
-} // namespace log
-} // namespace isc
-
-#endif // __LOGGER_LEVELS_H
diff --git a/src/lib/log/logger_manager.cc b/src/lib/log/logger_manager.cc
new file mode 100644
index 0000000..9955a83
--- /dev/null
+++ b/src/lib/log/logger_manager.cc
@@ -0,0 +1,183 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <algorithm>
+#include <vector>
+
+#include <log/logger.h>
+#include <log/logger_manager_impl.h>
+#include <log/logger_manager.h>
+#include <log/logger_name.h>
+#include <log/messagedef.h>
+#include <log/message_dictionary.h>
+#include <log/message_exception.h>
+#include <log/message_initializer.h>
+#include <log/message_reader.h>
+#include <log/message_types.h>
+#include <log/macros.h>
+#include <log/messagedef.h>
+#include <log/message_initializer.h>
+
+using namespace std;
+
+namespace {
+
+// Logger used for logging messages within the logging code itself.
+isc::log::Logger logger("log");
+
+// Static stores for the initialization severity and debug level.
+// These are put in methods to avoid a "static initialization fiasco".
+
+isc::log::Severity& initSeverity() {
+ static isc::log::Severity severity = isc::log::INFO;
+ return (severity);
+}
+
+int& initDebugLevel() {
+ static int dbglevel = 0;
+ return (dbglevel);
+}
+
+std::string& initRootName() {
+ static std::string root("bind10");
+ return (root);
+}
+
+} // Anonymous namespace
+
+
+namespace isc {
+namespace log {
+
+// Constructor - create the implementation class.
+LoggerManager::LoggerManager() {
+ impl_ = new LoggerManagerImpl();
+}
+
+// Destructor - get rid of the implementation class
+LoggerManager::~LoggerManager() {
+ delete impl_;
+}
+
+// Initialize processing
+void
+LoggerManager::processInit() {
+ impl_->processInit();
+}
+
+// Process logging specification
+void
+LoggerManager::processSpecification(const LoggerSpecification& spec) {
+ impl_->processSpecification(spec);
+}
+
+// End Processing
+void
+LoggerManager::processEnd() {
+ impl_->processEnd();
+}
+
+
+/// Logging system initialization
+
+void
+LoggerManager::init(const std::string& root, isc::log::Severity severity,
+ int dbglevel, const char* file)
+{
+ // Save name, severity and debug level for later. No need to save the
+ // file name as once the local message file is read the messages will
+ // not be lost.
+ initRootName() = root;
+ initSeverity() = severity;
+ initDebugLevel() = dbglevel;
+
+ // Create the BIND 10 root logger and set the default severity and
+ // debug level. This is the logger that has the name of the application.
+ // All other loggers created in this application will be its children.
+ setRootLoggerName(root);
+
+ // Initialize the implementation logging. After this point, some basic
+ // logging has been set up and messages can be logged.
+ LoggerManagerImpl::init(severity, dbglevel);
+
+ // Check if there were any duplicate message IDs in the default dictionary
+ // and if so, log them. Log using the logging facility logger.
+ vector<string>& duplicates = MessageInitializer::getDuplicates();
+ if (!duplicates.empty()) {
+
+ // There are duplicates present. This will be listed in alphabetic
+ // order of message ID, so they need to be sorted. This list itself may
+ // contain duplicates; if so, the message ID is listed as many times as
+ // there are duplicates.
+ sort(duplicates.begin(), duplicates.end());
+ for (vector<string>::iterator i = duplicates.begin();
+ i != duplicates.end(); ++i) {
+ LOG_WARN(logger, MSG_DUPMSGID).arg(*i);
+ }
+
+ }
+
+ // Replace any messages with local ones (if given)
+ if (file) {
+ readLocalMessageFile(file);
+ }
+}
+
+
+// Read local message file
+// TODO This should be done after the configuration has been read so that
+// the file can be placed in the local configuration
+void
+LoggerManager::readLocalMessageFile(const char* file) {
+
+ MessageDictionary& dictionary = MessageDictionary::globalDictionary();
+ MessageReader reader(&dictionary);
+ try {
+
+ logger.info(MSG_RDLOCMES).arg(file);
+ reader.readFile(file, MessageReader::REPLACE);
+
+ // File successfully read. As each message in the file is supposed to
+ // replace one in the dictionary, any ID read that can't be located in
+ // the dictionary will not be used. To aid problem diagnosis, the
+ // unknown message IDs are listed.
+ MessageReader::MessageIDCollection unknown = reader.getNotAdded();
+ for (MessageReader::MessageIDCollection::const_iterator
+ i = unknown.begin(); i != unknown.end(); ++i) {
+ string message_id = boost::lexical_cast<string>(*i);
+ logger.warn(MSG_IDNOTFND).arg(message_id);
+ }
+ }
+ catch (MessageException& e) {
+ MessageID ident = e.id();
+ vector<string> args = e.arguments();
+
+ // Log the variable number of arguments. The actual message will be
+ // logged when the error_message variable is destroyed.
+ Formatter<isc::log::Logger> error_message = logger.error(ident);
+ for (int i = 0; i < args.size(); ++i) {
+ error_message = error_message.arg(args[i]);
+ }
+ }
+}
+
+// Reset logging to settings passed to init()
+void
+LoggerManager::reset() {
+ setRootLoggerName(initRootName());
+ LoggerManagerImpl::reset(initSeverity(), initDebugLevel());
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_manager.h b/src/lib/log/logger_manager.h
new file mode 100644
index 0000000..dece0c9
--- /dev/null
+++ b/src/lib/log/logger_manager.h
@@ -0,0 +1,141 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_MANAGER_H
+#define __LOGGER_MANAGER_H
+
+#include "exceptions/exceptions.h"
+#include <log/logger_specification.h>
+
+// Generated if, when updating the logging specification, an unknown
+// destination is encountered.
+class UnknownLoggingDestination : public isc::Exception {
+public:
+ UnknownLoggingDestination(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what)
+ {}
+};
+
+namespace isc {
+namespace log {
+
+class LoggerManagerImpl;
+
+/// \brief Logger Manager
+///
+/// The logger manager class exists to process the set of logger specifications
+/// and use them to set up the logging in the program appropriately.
+///
+/// To isolate the underlying implementation from basic processing, the
+/// LoggerManager is implemented using the "pimpl" idiom.
+
+class LoggerManager {
+public:
+ /// \brief Constructor
+ LoggerManager();
+
+ /// \brief Destructor
+ ~LoggerManager();
+
+ /// \brief Process Specifications
+ ///
+ /// Replaces the current logging configuration by the one given.
+ ///
+ /// \param start Iterator pointing to the start of the collection of
+ /// logging specifications.
+ /// \param finish Iterator pointing to the end of the collection of
+ /// logging specification.
+ template <typename T>
+ void process(T start, T finish) {
+ processInit();
+ for (T i = start; i != finish; ++i) {
+ processSpecification(*i);
+ }
+ processEnd();
+ }
+
+ /// \brief Process a single specification
+ ///
+ /// A convenience function for a single specification.
+ ///
+ /// \param spec Specification to process
+ void process(const LoggerSpecification& spec) {
+ processInit();
+ processSpecification(spec);
+ processEnd();
+ }
+
+ /// \brief Run-Time Initialization
+ ///
+ /// Performs run-time initialization of the logger system, in particular
+ /// supplying the root logger name and name of a replacement message file.
+ ///
+ /// This must be the first logging function called in the program. If
+ /// an attempt is made to log a message before this is function is called,
+ /// the results will be dependent on the underlying logging package.
+ ///
+ /// \param root Name of the root logger. This should be set to the name of
+ /// the program.
+ /// \param severity Severity at which to log
+ /// \param dbglevel Debug severity (ignored if "severity" is not "DEBUG")
+ /// \param file Name of the local message file. This must be NULL if there
+ /// is no local message file.
+ static void init(const std::string& root,
+ isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0, const char* file = NULL);
+
+ /// \brief Reset logging
+ ///
+ /// Resets logging to whatever was set in the call to init().
+ static void reset();
+
+ /// \brief Read local message file
+ ///
+ /// Reads the local message file into the global dictionary, overwriting
+ /// existing messages. If the file contained any message IDs not in the
+ /// dictionary, they are listed in a warning message.
+ ///
+ /// \param file Name of the local message file
+ static void readLocalMessageFile(const char* file);
+
+private:
+ /// \brief Initialize Processing
+ ///
+ /// Initializes the processing of a list of specifications by resetting all
+ /// loggers to their defaults, which is to pass the message to their
+ /// parent logger. (Except for the root logger, where the default action is
+ /// to output the message.)
+ void processInit();
+
+ /// \brief Process Logging Specification
+ ///
+ /// Processes the given specification. It is assumed at this point that
+ /// either the logger does not exist or has been made inactive.
+ void processSpecification(const LoggerSpecification& spec);
+
+ /// \brief End Processing
+ ///
+ /// Place holder for finish processing.
+ /// TODO: Check that the root logger has something enabled
+ void processEnd();
+
+ // Members
+ LoggerManagerImpl* impl_; ///< Pointer to implementation
+};
+
+} // namespace log
+} // namespace isc
+
+
+#endif // __LOGGER_MANAGER_H
diff --git a/src/lib/log/logger_manager_impl.cc b/src/lib/log/logger_manager_impl.cc
new file mode 100644
index 0000000..92806d0
--- /dev/null
+++ b/src/lib/log/logger_manager_impl.cc
@@ -0,0 +1,228 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <algorithm>
+#include <iostream>
+
+#include <log4cplus/logger.h>
+#include <log4cplus/configurator.h>
+#include <log4cplus/consoleappender.h>
+#include <log4cplus/fileappender.h>
+#include <log4cplus/syslogappender.h>
+
+#include "log/logger.h"
+#include "log/logger_level_impl.h"
+#include "log/logger_manager.h"
+#include "log/logger_manager_impl.h"
+#include "log/logger_name.h"
+#include "log/logger_specification.h"
+#include "log/messagedef.h"
+
+using namespace std;
+
+namespace isc {
+namespace log {
+
+// Reset hierarchy of loggers back to default settings. This removes all
+// appenders from loggers, sets their severity to NOT_SET (so that events are
+// passed back to the parent) and resets the root logger to logging
+// informational messages. (This last is not a log4cplus default, so we have to
+// explicitly reset the logging severity.)
+
+void
+LoggerManagerImpl::processInit() {
+ log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
+ initRootLogger();
+}
+
+// Process logging specification. Set up the common states then dispatch to
+// add output specifications.
+
+void
+LoggerManagerImpl::processSpecification(const LoggerSpecification& spec) {
+
+ log4cplus::Logger logger = log4cplus::Logger::getInstance(
+ expandLoggerName(spec.getName()));
+
+ // Set severity level according to specification entry.
+ logger.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
+ Level(spec.getSeverity(), spec.getDbglevel())));
+
+ // Set the additive flag.
+ logger.setAdditivity(spec.getAdditive());
+
+ // Output options given?
+ if (spec.optionCount() > 0) {
+
+ // Yes, so replace all appenders for this logger.
+ logger.removeAllAppenders();
+
+ // Now process output specifications.
+ for (LoggerSpecification::const_iterator i = spec.begin();
+ i != spec.end(); ++i) {
+ switch (i->destination) {
+ case OutputOption::DEST_CONSOLE:
+ createConsoleAppender(logger, *i);
+ break;
+
+ case OutputOption::DEST_FILE:
+ createFileAppender(logger, *i);
+ break;
+
+ case OutputOption::DEST_SYSLOG:
+ createSyslogAppender(logger, *i);
+ break;
+
+ default:
+ // Not a valid destination. As we are in the middle of updating
+ // logging destinations, we could be in the situation where
+ // there are no valid appenders. For this reason, throw an
+ // exception.
+ isc_throw(UnknownLoggingDestination,
+ "Unknown logging destination, code = " <<
+ i->destination);
+ }
+ }
+ }
+}
+
+// Console appender - log to either stdout or stderr.
+void
+LoggerManagerImpl::createConsoleAppender(log4cplus::Logger& logger,
+ const OutputOption& opt)
+{
+ log4cplus::SharedAppenderPtr console(
+ new log4cplus::ConsoleAppender(
+ (opt.stream == OutputOption::STR_STDERR), opt.flush));
+ setConsoleAppenderLayout(console);
+ logger.addAppender(console);
+}
+
+// File appender. Depending on whether a maximum size is given, either
+// a standard file appender or a rolling file appender will be created.
+void
+LoggerManagerImpl::createFileAppender(log4cplus::Logger& logger,
+ const OutputOption& opt)
+{
+ LOG4CPLUS_OPEN_MODE_TYPE mode =
+ LOG4CPLUS_FSTREAM_NAMESPACE::ios::app; // Append to existing file
+
+ log4cplus::SharedAppenderPtr fileapp;
+ if (opt.maxsize == 0) {
+ fileapp = log4cplus::SharedAppenderPtr(new log4cplus::FileAppender(
+ opt.filename, mode, opt.flush));
+ } else {
+ fileapp = log4cplus::SharedAppenderPtr(
+ new log4cplus::RollingFileAppender(opt.filename, opt.maxsize,
+ opt.maxver, opt.flush));
+ }
+
+ // use the same console layout for the files.
+ setConsoleAppenderLayout(fileapp);
+ logger.addAppender(fileapp);
+}
+
+// Syslog appender.
+void
+LoggerManagerImpl::createSyslogAppender(log4cplus::Logger& logger,
+ const OutputOption& opt)
+{
+ log4cplus::SharedAppenderPtr syslogapp(
+ new log4cplus::SysLogAppender(opt.facility));
+ setSyslogAppenderLayout(syslogapp);
+ logger.addAppender(syslogapp);
+}
+
+
+// One-time initialization of the log4cplus system
+
+void
+LoggerManagerImpl::init(isc::log::Severity severity, int dbglevel) {
+
+ // Set up basic configurator. This attaches a ConsoleAppender to the
+ // root logger with suitable output. This is used until we we have
+ // actually read the logging configuration, in which case the output
+ // may well be changed.
+ log4cplus::BasicConfigurator config;
+ config.configure();
+
+ // Add the additional debug levels
+ LoggerLevelImpl::init();
+
+ reset(severity, dbglevel);
+}
+
+// Reset logging to default configuration. This closes all appenders
+// and resets the root logger to output INFO messages to the console.
+// It is principally used in testing.
+void
+LoggerManagerImpl::reset(isc::log::Severity severity, int dbglevel) {
+
+ // Initialize the root logger
+ initRootLogger(severity, dbglevel);
+}
+
+// Initialize the root logger
+void LoggerManagerImpl::initRootLogger(isc::log::Severity severity,
+ int dbglevel)
+{
+ log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
+
+ // Set the log4cplus root to not output anything - effectively we are
+ // ignoring it.
+ log4cplus::Logger::getRoot().setLogLevel(log4cplus::OFF_LOG_LEVEL);
+
+ // Set the level for the BIND 10 root logger to the given severity and
+ // debug level.
+ log4cplus::Logger b10root = log4cplus::Logger::getInstance(
+ getRootLoggerName());
+ b10root.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
+ Level(severity, dbglevel)));
+
+ // Set the BIND 10 root to use a console logger.
+ OutputOption opt;
+ createConsoleAppender(b10root, opt);
+}
+
+// Set the the "console" layout for the given appenders. This layout includes
+// a date/time and the name of the logger.
+
+void LoggerManagerImpl::setConsoleAppenderLayout(
+ log4cplus::SharedAppenderPtr& appender)
+{
+ // Create the pattern we want for the output - local time.
+ string pattern = "%D{%Y-%m-%d %H:%M:%S.%q} %-5p [%c] %m\n";
+
+ // Finally the text of the message
+ auto_ptr<log4cplus::Layout> layout(new log4cplus::PatternLayout(pattern));
+ appender->setLayout(layout);
+}
+
+// Set the the "syslog" layout for the given appenders. This is the same
+// as the console, but without the timestamp (which is expected to be
+// set by syslogd).
+
+void LoggerManagerImpl::setSyslogAppenderLayout(
+ log4cplus::SharedAppenderPtr& appender)
+{
+ // Create the pattern we want for the output - local time.
+ string pattern = "%-5p [%c] %m\n";
+
+ // Finally the text of the message
+ auto_ptr<log4cplus::Layout> layout(new log4cplus::PatternLayout(pattern));
+ appender->setLayout(layout);
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_manager_impl.h b/src/lib/log/logger_manager_impl.h
new file mode 100644
index 0000000..aa596a0
--- /dev/null
+++ b/src/lib/log/logger_manager_impl.h
@@ -0,0 +1,171 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_MANAGER_IMPL_H
+#define __LOGGER_MANAGER_IMPL_H
+
+#include <string>
+
+#include <log4cplus/appender.h>
+#include <log/logger_level.h>
+
+// Forward declaration to avoid need to include log4cplus header file here.
+namespace log4cplus {
+class Logger;
+class Appender;
+}
+
+namespace isc {
+namespace log {
+
+// Forward declarations
+class LoggerSpecification;
+class OutputOption;
+
+/// \brief Logger Manager Implementation
+///
+/// This is the implementation of the logger manager for the log4cplus
+/// underlying logger.
+///
+/// As noted in logger_manager.h, the logger manager class exists to set up the
+/// logging given a set of specifications. This class handles the processing
+/// of those specifications.
+///
+/// Note: the logging has been implemented using a "pimpl" idiom to conceal
+/// the underlying implementation (log4cplus) from the BIND 10 interface.
+/// This requires that there be an implementation class, even though in this
+/// case, all the implementation class methods can be declared static.
+
+class LoggerManagerImpl {
+public:
+
+ /// \brief Constructor
+ LoggerManagerImpl()
+ {}
+
+ /// \brief Initialize Processing
+ ///
+ /// This resets the hierachy of loggers back to their defaults. This means
+ /// that all non-root loggers (if they exist) are set to NOT_SET, and the
+ /// root logger reset to logging informational messages.
+ ///
+ /// \param root_name BIND 10 name of the root logger
+ static void processInit();
+
+ /// \brief Process Specification
+ ///
+ /// Processes the specification for a single logger.
+ ///
+ /// \param spec Logging specification for this logger
+ static void processSpecification(const LoggerSpecification& spec);
+
+ /// \brief End Processing
+ ///
+ /// Terminates the processing of the logging specifications.
+ static void processEnd()
+ {}
+
+ /// \brief Implementation-specific initialization
+ ///
+ /// Sets the basic configuration for logging (the root logger has INFO and
+ /// more severe messages routed to stdout). Unless this function (or
+ /// process() with a valid specification for all loggers that will log
+ /// messages) is called before a message is logged, log4cplus will output
+ /// a message to stderr noting that logging has not been initialized.
+ ///
+ /// It is assumed here that the name of the BIND 10 root logger can be
+ /// obtained from the global function getRootLoggerName().
+ ///
+ /// \param severity Severity to be associated with this logger
+ /// \param dbglevel Debug level associated with the root logger
+ static void init(isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0);
+
+ /// \brief Reset logging
+ ///
+ /// Resets to default configuration (root logger logging to the console
+ /// with INFO severity).
+ ///
+ /// \param severity Severity to be associated with this logger
+ /// \param dbglevel Debug level associated with the root logger
+ static void reset(isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0);
+
+private:
+ /// \brief Create console appender
+ ///
+ /// Creates an object that, when attached to a logger, will log to one
+ /// of the output streams (stdout or stderr).
+ ///
+ /// \param logger Log4cplus logger to which the appender must be attached.
+ /// \param opt Output options for this appender.
+ static void createConsoleAppender(log4cplus::Logger& logger,
+ const OutputOption& opt);
+
+ /// \brief Create file appender
+ ///
+ /// Creates an object that, when attached to a logger, will log to a
+ /// specified file. This also includes the ability to "roll" files when
+ /// they reach a specified size.
+ ///
+ /// \param logger Log4cplus logger to which the appender must be attached.
+ /// \param opt Output options for this appender.
+ static void createFileAppender(log4cplus::Logger& logger,
+ const OutputOption& opt);
+
+ /// \brief Create syslog appender
+ ///
+ /// Creates an object that, when attached to a logger, will log to the
+ /// syslog file.
+ ///
+ /// \param logger Log4cplus logger to which the appender must be attached.
+ /// \param opt Output options for this appender.
+ static void createSyslogAppender(log4cplus::Logger& logger,
+ const OutputOption& opt);
+
+ /// \brief Set default layout and severity for root logger
+ ///
+ /// Initializes the root logger to BIND 10 defaults - console output and
+ /// the passed severity/debug level.
+ ///
+ /// \param severity Severity of messages that the logger should output.
+ /// \param dbglevel Debug level if severity = DEBUG
+ static void initRootLogger(isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0);
+
+ /// \brief Set layout for console appender
+ ///
+ /// Sets the layout of the specified appender to one suitable for file
+ /// or console output:
+ ///
+ /// YYYY-MM-DD HH:MM:SS.ssss SEVERITY [root.logger] message
+ ///
+ /// \param appender Appender for which this pattern is to be set.
+ static void setConsoleAppenderLayout(log4cplus::SharedAppenderPtr& appender);
+
+ /// \brief Set layout for syslog appender
+ ///
+ /// Sets the layout of the specified appender to one suitable for the
+ /// syslog file:
+ ///
+ /// SEVERITY [root.logger] message
+ ///
+ /// \param appender Appender for which this pattern is to be set.
+ static void setSyslogAppenderLayout(log4cplus::SharedAppenderPtr& appender);
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __LOGGER_MANAGER_IMPL_H
diff --git a/src/lib/log/logger_name.cc b/src/lib/log/logger_name.cc
new file mode 100644
index 0000000..abfcd5e
--- /dev/null
+++ b/src/lib/log/logger_name.cc
@@ -0,0 +1,59 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+#include "log/logger_name.h"
+
+namespace isc {
+namespace log {
+
+namespace {
+
+// Obtain the root logger name in a way that is safe for statically-initialized
+// objects.
+
+std::string&
+getRootLoggerNameInternal() {
+ static std::string root_name;
+ return (root_name);
+}
+
+} // Anonymous namespace
+
+void
+setRootLoggerName(const std::string& name) {
+ getRootLoggerNameInternal() = name;
+}
+
+const std::string& getRootLoggerName() {
+ return (getRootLoggerNameInternal());
+}
+
+std::string expandLoggerName(const std::string& name) {
+
+ // Are we the root logger, or does the logger name start with
+ // the string "<root_logger_name>.". If so, use a logger
+ // whose name is the one given.
+ if ((name == getRootLoggerName()) ||
+ (name.find(getRootLoggerName() + std::string(".")) == 0)) {
+ return (name);
+
+ }
+
+ // Anything else is assumed to be a sub-logger of the root logger.
+ return (getRootLoggerName() + "." + name);
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_name.h b/src/lib/log/logger_name.h
new file mode 100644
index 0000000..82ea2ad
--- /dev/null
+++ b/src/lib/log/logger_name.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_NAME_H
+#define __LOGGER_NAME_H
+
+#include <string>
+
+/// \brief Define Name of Root Logger
+///
+/// In BIND-10, the name root logger of a program is the name of the program
+/// itself (in contrast to packages such as log4cplus where the root logger name
+// is something like "root"). These trivial functions allow the setting and
+// getting of that name by the logger classes.
+
+namespace isc {
+namespace log {
+
+/// \brief Set root logger name
+///
+/// This function should be called by the program's initialization code before
+/// any logging functions are called.
+///
+/// \param name Name of the root logger. This should be the program name.
+void setRootLoggerName(const std::string& name);
+
+/// \brief Get root logger name
+///
+/// \return Name of the root logger.
+const std::string& getRootLoggerName();
+
+/// \brief Expand logger name
+///
+/// Given a logger name, returns the fully-expanded logger name. If the name
+/// starts with the root logger name, it is returned as-is. Otherwise it is
+/// prefixed with the root logger name.
+///
+/// \param name Name to expand.
+///
+/// \return Fully-expanded logger name.
+std::string expandLoggerName(const std::string& name);
+
+}
+}
+
+#endif // __LOGGER_NAME_H
diff --git a/src/lib/log/logger_specification.h b/src/lib/log/logger_specification.h
new file mode 100644
index 0000000..35c879c
--- /dev/null
+++ b/src/lib/log/logger_specification.h
@@ -0,0 +1,156 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_SPECIFICATION_H
+#define __LOGGER_SPECIFICATION_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <log/logger_level.h>
+#include <log/output_option.h>
+
+/// \brief Logger Specification
+///
+/// The logging configuration options are a list of logger specifications, each
+/// of which represents a logger and the options for its appenders.
+///
+/// Unlike OutputOption (which is a struct), this contains a bit more
+/// structure and is concealed in a class.
+
+#include <vector>
+
+namespace isc {
+namespace log {
+
+class LoggerSpecification {
+public:
+ typedef std::vector<OutputOption>::iterator iterator;
+ typedef std::vector<OutputOption>::const_iterator const_iterator;
+
+ /// \brief Constructor
+ ///
+ /// \param name Name of the logger.
+ /// \param severity Severity at which this logger logs
+ /// \param dbglevel Debug level
+ /// \param additive true to cause message logged with this logger to be
+ /// passed to the parent for logging.
+ LoggerSpecification(const std::string& name = "",
+ isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0, bool additive = false) :
+ name_(name), severity_(severity), dbglevel_(dbglevel),
+ additive_(additive)
+ {}
+
+ /// \brief Set the name of the logger.
+ ///
+ /// \param name Name of the logger.
+ void setName(const std::string& name) {
+ name_ = name;
+ }
+
+ /// \return Return logger name.
+ std::string getName() const {
+ return name_;
+ }
+
+ /// \brief Set the severity.
+ ///
+ /// \param severity New severity of the logger.
+ void setSeverity(isc::log::Severity severity) {
+ severity_ = severity;
+ }
+
+ /// \return Return logger severity.
+ isc::log::Severity getSeverity() const {
+ return severity_;
+ }
+
+ /// \brief Set the debug level.
+ ///
+ /// \param dbglevel New debug level of the logger.
+ void setDbglevel(int dbglevel) {
+ dbglevel_ = dbglevel;
+ }
+
+ /// \return Return logger debug level
+ int getDbglevel() const {
+ return dbglevel_;
+ }
+
+ /// \brief Set the additive flag.
+ ///
+ /// \param additive New value of the additive flag.
+ void setAdditive(bool additive) {
+ additive_ = additive;
+ }
+
+ /// \return Return additive flag.
+ int getAdditive() const {
+ return additive_;
+ }
+
+ /// \brief Add output option.
+ ///
+ /// \param Option to add to the list.
+ void addOutputOption(const OutputOption& option) {
+ options_.push_back(option);
+ }
+
+ /// \return Iterator to start of output options.
+ iterator begin() {
+ return options_.begin();
+ }
+
+ /// \return Iterator to start of output options.
+ const_iterator begin() const {
+ return options_.begin();
+ }
+
+ /// \return Iterator to end of output options.
+ iterator end() {
+ return options_.end();
+ }
+
+ /// \return Iterator to end of output options.
+ const_iterator end() const {
+ return options_.end();
+ }
+
+ /// \return Number of output specification options.
+ size_t optionCount() const {
+ return options_.size();
+ }
+
+ /// \brief Reset back to defaults.
+ void reset() {
+ name_ = "";
+ severity_ = isc::log::INFO;
+ dbglevel_ = 0;
+ additive_ = false;
+ options_.clear();
+ }
+
+private:
+ std::string name_; ///< Logger name
+ isc::log::Severity severity_; ///< Severity for this logger
+ int dbglevel_; ///< Debug level
+ bool additive_; ///< Chaining output
+ std::vector<OutputOption> options_; ///< Logger options
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __LOGGER_SPEC_IFICATIONH
diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc
index e17c47d..78e28f3 100644
--- a/src/lib/log/logger_support.cc
+++ b/src/lib/log/logger_support.cc
@@ -28,18 +28,10 @@
#include <algorithm>
#include <iostream>
#include <string>
-#include <vector>
-#include <boost/lexical_cast.hpp>
#include <log/logger.h>
+#include <log/logger_manager.h>
#include <log/logger_support.h>
-#include <log/messagedef.h>
-#include <log/message_dictionary.h>
-#include <log/message_exception.h>
-#include <log/message_initializer.h>
-#include <log/message_reader.h>
-#include <log/message_types.h>
-#include <log/root_logger_name.h>
namespace isc {
namespace log {
@@ -50,96 +42,20 @@ using namespace std;
// root logger and is used in all functions in this file.
Logger logger("log");
-
-/// \brief Reads Local Message File
-///
-/// Reads the local message file into the global dictionary, overwriting
-/// existing messages. If the file contained any message IDs not in the
-/// dictionary, they are listed in a warning message.
-///
-/// \param file Name of the local message file
-static void
-readLocalMessageFile(const char* file) {
-
- MessageDictionary& dictionary = MessageDictionary::globalDictionary();
- MessageReader reader(&dictionary);
- try {
- logger.info(MSG_RDLOCMES).arg(file);
- reader.readFile(file, MessageReader::REPLACE);
-
- // File successfully read, list the duplicates
- MessageReader::MessageIDCollection unknown = reader.getNotAdded();
- for (MessageReader::MessageIDCollection::const_iterator
- i = unknown.begin(); i != unknown.end(); ++i) {
- string message_id = boost::lexical_cast<string>(*i);
- logger.warn(MSG_IDNOTFND).arg(message_id);
- }
- }
- catch (MessageException& e) {
- MessageID ident = e.id();
- vector<string> args = e.arguments();
- switch (args.size()) {
- case 0:
- logger.error(ident);
- break;
-
- case 1:
- logger.error(ident).arg(args[0]);
- break;
-
- case 2:
- logger.error(ident).arg(args[0]).arg(args[1]);
- break;
-
- default: // 3 or more (3 should be the maximum)
- logger.error(ident).arg(args[0]).arg(args[1]).arg(args[2]);
- }
- }
-}
-
/// Logger Run-Time Initialization
void
initLogger(const string& root, isc::log::Severity severity, int dbglevel,
const char* file) {
-
- // Create the application root logger and set the default severity and
- // debug level. This is the logger that has the name of the application.
- // All other loggers created in this application will be its children.
- setRootLoggerName(root);
- Logger root_logger(isc::log::getRootLoggerName(), true);
-
- // Set the severity associated with it. If no other logger has a severity,
- // this will be the default.
- root_logger.setSeverity(severity, dbglevel);
-
- // Check if there were any duplicate message IDs in the default dictionary
- // and if so, log them. Log using the logging facility root logger.
- vector<string>& duplicates = MessageInitializer::getDuplicates();
- if (!duplicates.empty()) {
-
- // There are - sort and remove any duplicates.
- sort(duplicates.begin(), duplicates.end());
- vector<string>::iterator new_end =
- unique(duplicates.begin(), duplicates.end());
- for (vector<string>::iterator i = duplicates.begin(); i != new_end; ++i) {
- logger.warn(MSG_DUPMSGID).arg(*i);
- }
-
- }
-
- // Replace any messages with local ones (if given)
- if (file) {
- readLocalMessageFile(file);
- }
+ LoggerManager::init(root, severity, dbglevel, file);
}
/// Logger Run-Time Initialization via Environment Variables
-void initLogger() {
+void initLogger(isc::log::Severity severity, int dbglevel) {
// Root logger name is defined by the environment variable B10_LOGGER_ROOT.
- // If not present, the name is "b10root".
- const char* DEFAULT_ROOT = "b10root";
+ // If not present, the name is "bind10".
+ const char* DEFAULT_ROOT = "bind10";
const char* root = getenv("B10_LOGGER_ROOT");
if (! root) {
root = DEFAULT_ROOT;
@@ -149,29 +65,13 @@ void initLogger() {
// B10_LOGGER_SEVERITY, and can be one of "DEBUG", "INFO", "WARN", "ERROR"
// of "FATAL". Note that the string must be in upper case with no leading
// of trailing blanks.
- isc::log::Severity severity = isc::log::DEFAULT;
const char* sev_char = getenv("B10_LOGGER_SEVERITY");
if (sev_char) {
- string sev_string(sev_char);
- if (sev_string == "DEBUG") {
- severity = isc::log::DEBUG;
- } else if (sev_string == "INFO") {
- severity = isc::log::INFO;
- } else if (sev_string == "WARN") {
- severity = isc::log::WARN;
- } else if (sev_string == "ERROR") {
- severity = isc::log::ERROR;
- } else if (sev_string == "FATAL") {
- severity = isc::log::FATAL;
- } else {
- std::cerr << "**ERROR** unrecognised logger severity of '"
- << sev_string << "' - default severity will be used\n";
- }
+ severity = isc::log::getSeverity(sev_char);
}
// If the severity is debug, get the debug level (environment variable
// B10_LOGGER_DBGLEVEL), which should be in the range 0 to 99.
- int dbglevel = 0;
if (severity == isc::log::DEBUG) {
const char* dbg_char = getenv("B10_LOGGER_DBGLEVEL");
if (dbg_char) {
diff --git a/src/lib/log/logger_support.h b/src/lib/log/logger_support.h
index f4861b2..5d574d3 100644
--- a/src/lib/log/logger_support.h
+++ b/src/lib/log/logger_support.h
@@ -15,6 +15,8 @@
#ifndef __LOGGER_SUPPORT_H
#define __LOGGER_SUPPORT_H
+#include <unistd.h>
+
#include <string>
#include <log/logger.h>
@@ -36,8 +38,9 @@ namespace log {
/// \param severity Severity at which to log
/// \param dbglevel Debug severity (ignored if "severity" is not "DEBUG")
/// \param file Name of the local message file.
-void initLogger(const std::string& root, isc::log::Severity severity,
- int dbglevel, const char* file);
+void initLogger(const std::string& root,
+ isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0, const char* file = NULL);
/// \brief Run-Time Initialization from Environment
@@ -46,19 +49,20 @@ void initLogger(const std::string& root, isc::log::Severity severity,
/// environment variables. These are:
///
/// B10_LOGGER_ROOT
-/// Name of the root logger. If not given, the string "b10root" will be used.
+/// Name of the root logger. If not given, the string "bind10" will be used.
///
/// B10_LOGGER_SEVERITY
/// Severity of messages that will be logged. This must be one of the strings
-/// "DEBUG", "INFO", "WARN", "ERROR", "FATAL". (Must be upper case and must
-/// not contain leading or trailing spaces.) If not specified (or if
-/// specified but incorrect), the default for the logging system will be used
-/// (currently INFO).
+/// "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE". (Must be upper case
+/// and must not contain leading or trailing spaces.) If not specified (or if
+/// specified but incorrect), the default passed as argument to this function
+/// (currently INFO) will be used.
///
/// B10_LOGGER_DBGLEVEL
/// Ignored if the level is not DEBUG, this should be a number between 0 and
/// 99 indicating the logging severity. The default is 0. If outside these
-/// limits or if not a number, a value of 0 is used.
+/// limits or if not a number, The value passed to this function (default
+/// of 0) is used.
///
/// B10_LOGGER_LOCALMSG
/// If defined, the path specification of a file that contains message
@@ -68,7 +72,8 @@ void initLogger(const std::string& root, isc::log::Severity severity,
///
/// This function is most likely to be called from unit test programs.
-void initLogger();
+void initLogger(isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0);
} // namespace log
} // namespace isc
diff --git a/src/lib/log/messagedef.cc b/src/lib/log/messagedef.cc
index 5cc89b3..853722a 100644
--- a/src/lib/log/messagedef.cc
+++ b/src/lib/log/messagedef.cc
@@ -1,4 +1,4 @@
-// File created from messagedef.mes on Mon May 9 13:52:54 2011
+// File created from messagedef.mes on Fri May 27 14:49:45 2011
#include <cstddef>
#include <log/message_types.h>
@@ -7,6 +7,9 @@
namespace isc {
namespace log {
+extern const isc::log::MessageID MSG_BADDESTINATION = "MSG_BADDESTINATION";
+extern const isc::log::MessageID MSG_BADSEVERITY = "MSG_BADSEVERITY";
+extern const isc::log::MessageID MSG_BADSTREAM = "MSG_BADSTREAM";
extern const isc::log::MessageID MSG_DUPLNS = "MSG_DUPLNS";
extern const isc::log::MessageID MSG_DUPMSGID = "MSG_DUPMSGID";
extern const isc::log::MessageID MSG_IDNOTFND = "MSG_IDNOTFND";
@@ -31,6 +34,9 @@ extern const isc::log::MessageID MSG_WRITERR = "MSG_WRITERR";
namespace {
const char* values[] = {
+ "MSG_BADDESTINATION", "unrecognized log destination: %1",
+ "MSG_BADSEVERITY", "unrecognized log severity: %1",
+ "MSG_BADSTREAM", "bad log console output stream: %1",
"MSG_DUPLNS", "line %1: duplicate $NAMESPACE directive found",
"MSG_DUPMSGID", "duplicate message ID (%1) in compiled code",
"MSG_IDNOTFND", "could not replace message text for '%1': no such message",
diff --git a/src/lib/log/messagedef.h b/src/lib/log/messagedef.h
index 79c8bab..bdb1075 100644
--- a/src/lib/log/messagedef.h
+++ b/src/lib/log/messagedef.h
@@ -1,4 +1,4 @@
-// File created from messagedef.mes on Mon May 9 13:52:54 2011
+// File created from messagedef.mes on Fri May 27 14:49:45 2011
#ifndef __MESSAGEDEF_H
#define __MESSAGEDEF_H
@@ -8,6 +8,9 @@
namespace isc {
namespace log {
+extern const isc::log::MessageID MSG_BADDESTINATION;
+extern const isc::log::MessageID MSG_BADSEVERITY;
+extern const isc::log::MessageID MSG_BADSTREAM;
extern const isc::log::MessageID MSG_DUPLNS;
extern const isc::log::MessageID MSG_DUPMSGID;
extern const isc::log::MessageID MSG_IDNOTFND;
diff --git a/src/lib/log/messagedef.mes b/src/lib/log/messagedef.mes
index 51c04fa..a54931b 100644
--- a/src/lib/log/messagedef.mes
+++ b/src/lib/log/messagedef.mes
@@ -117,3 +117,15 @@ the named output file.
% UNRECDIR line %1: unrecognised directive '%2'
A line starting with a dollar symbol was found, but the first word on the line
(shown in the message) was not a recognised message compiler directive.
+
+% BADSEVERITY unrecognized log severity: %1
+A logger severity value was given that was not recognized. The severity
+should be one of "DEBUG", "INFO", "WARN", "ERROR", or "FATAL".
+
+% BADDESTINATION unrecognized log destination: %1
+A logger destination value was given that was not recognized. The
+destination should be one of "console", "file", or "syslog".
+
+% BADSTREAM bad log console output stream: %1
+A log console output stream was given that was not recognized. The
+output stream should be one of "stdout", or "stderr"
diff --git a/src/lib/log/output_option.cc b/src/lib/log/output_option.cc
new file mode 100644
index 0000000..191631d
--- /dev/null
+++ b/src/lib/log/output_option.cc
@@ -0,0 +1,54 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+#include <log/output_option.h>
+#include <log/macros.h>
+#include <log/messagedef.h>
+
+#include <boost/algorithm/string.hpp>
+
+namespace isc {
+namespace log {
+
+OutputOption::Destination
+getDestination(const std::string& dest_str) {
+ if (boost::iequals(dest_str, "console")) {
+ return OutputOption::DEST_CONSOLE;
+ } else if (boost::iequals(dest_str, "file")) {
+ return OutputOption::DEST_FILE;
+ } else if (boost::iequals(dest_str, "syslog")) {
+ return OutputOption::DEST_SYSLOG;
+ } else {
+ Logger logger("log");
+ LOG_ERROR(logger, MSG_BADDESTINATION).arg(dest_str);
+ return OutputOption::DEST_CONSOLE;
+ }
+}
+
+OutputOption::Stream
+getStream(const std::string& stream_str) {
+ if (boost::iequals(stream_str, "stderr")) {
+ return OutputOption::STR_STDERR;
+ } else if (boost::iequals(stream_str, "stdout")) {
+ return OutputOption::STR_STDOUT;
+ } else {
+ Logger logger("log");
+ LOG_ERROR(logger, MSG_BADSTREAM).arg(stream_str);
+ return OutputOption::STR_STDOUT;
+ }
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/output_option.h b/src/lib/log/output_option.h
new file mode 100644
index 0000000..cbb7e95
--- /dev/null
+++ b/src/lib/log/output_option.h
@@ -0,0 +1,85 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __OUTPUT_OPTION_H
+#define __OUTPUT_OPTION_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string>
+
+/// \brief Logger Output Option
+///
+/// The logging configuration options are a list of logger specifications, each
+/// with one or more output options. This class represents an output option;
+/// one or more of these are attached to a LoggerSpecification object which is
+/// then passed to the LoggerManager to configure the logger.
+///
+/// Although there are three distinct output types (console, file, syslog) and
+/// the options for each do not really overlap. Although it is tempting to
+/// define a base OutputOption class and derive a class for each type
+/// (ConsoleOutputOptions etc.), it would be messy to use in practice. At
+/// some point the exact class would have to be known to get the class-specific
+/// options and the (pointer to) the base class cast to the appropriate type.
+/// Instead, this "struct" contains the union of all output options; it is up
+/// to the caller to cherry-pick the members it needs.
+///
+/// One final note: this object holds data and does no computation. For this
+/// reason, it is a "struct" and members are accessed directly instead of
+/// through methods.
+
+namespace isc {
+namespace log {
+
+struct OutputOption {
+
+ /// Destinations. Prefixed "DEST_" to avoid problems with the C stdio.h
+ /// FILE type.
+ typedef enum {
+ DEST_CONSOLE = 0,
+ DEST_FILE = 1,
+ DEST_SYSLOG = 2
+ } Destination;
+
+ /// If console, stream on which messages are output
+ typedef enum {
+ STR_STDOUT = 1,
+ STR_STDERR = 2
+ } Stream;
+
+ /// \brief Constructor
+ OutputOption() : destination(DEST_CONSOLE), stream(STR_STDERR),
+ flush(false), facility("LOCAL0"), filename(""),
+ maxsize(0), maxver(0)
+ {}
+
+ /// Members.
+
+ Destination destination; ///< Where the output should go
+ Stream stream; ///< stdout/stderr if console output
+ bool flush; ///< true to flush after each message
+ std::string facility; ///< syslog facility
+ std::string filename; ///< Filename if file output
+ size_t maxsize; ///< 0 if no maximum size
+ unsigned int maxver; ///< Maximum versions (none if <= 0)
+};
+
+OutputOption::Destination getDestination(const std::string& dest_str);
+OutputOption::Stream getStream(const std::string& stream_str);
+
+
+} // namespace log
+} // namespace isc
+
+#endif // __OUTPUT_OPTION_H
diff --git a/src/lib/log/root_logger_name.cc b/src/lib/log/root_logger_name.cc
deleted file mode 100644
index 58d9407..0000000
--- a/src/lib/log/root_logger_name.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <string>
-#include <root_logger_name.h>
-
-namespace isc {
-namespace log {
-
-namespace {
-
-// Obtain the root logger name in a way that is safe for statically-initialized
-// objects.
-
-std::string&
-getRootLoggerNameInternal() {
- static std::string root_name;
- return (root_name);
-}
-
-} // Anonymous namespace
-
-void
-setRootLoggerName(const std::string& name) {
- getRootLoggerNameInternal() = name;
-}
-
-const std::string& getRootLoggerName() {
- return (getRootLoggerNameInternal());
-}
-
-} // namespace log
-} // namespace isc
diff --git a/src/lib/log/root_logger_name.h b/src/lib/log/root_logger_name.h
deleted file mode 100644
index 9d50332..0000000
--- a/src/lib/log/root_logger_name.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __ROOT_LOGGER_NAME_H
-#define __ROOT_LOGGER_NAME_H
-
-#include <string>
-
-/// \brief Define Name of Root Logger
-///
-/// In BIND-10, the name root logger of a program is the name of the program
-/// itself (in contrast to packages such as log4cxx where the root logger name
-// is something like "."). These trivial functions allow the setting and
-// getting of that name by the logger classes.
-
-namespace isc {
-namespace log {
-
-/// \brief Set Root Logger Name
-///
-/// This function should be called by the program's initialization code before
-/// any logging functions are called.
-///
-/// \param name Name of the root logger. This should be the program name.
-void setRootLoggerName(const std::string& name);
-
-/// \brief Get Root Logger Name
-///
-/// \return Name of the root logger.
-const std::string& getRootLoggerName();
-
-}
-}
-
-#endif // __ROOT_LOGGER_NAME_H
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
index 46065e8..93703c1 100644
--- a/src/lib/log/tests/Makefile.am
+++ b/src/lib/log/tests/Makefile.am
@@ -2,8 +2,6 @@ SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
@@ -15,33 +13,50 @@ CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
TESTS += run_unittests
-run_unittests_SOURCES = root_logger_name_unittest.cc
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += log_formatter_unittest.cc
+run_unittests_SOURCES += logger_level_impl_unittest.cc
+run_unittests_SOURCES += logger_level_unittest.cc
+run_unittests_SOURCES += logger_manager_unittest.cc
+run_unittests_SOURCES += logger_name_unittest.cc
run_unittests_SOURCES += logger_unittest.cc
+run_unittests_SOURCES += logger_specification_unittest.cc
run_unittests_SOURCES += message_dictionary_unittest.cc
-run_unittests_SOURCES += message_reader_unittest.cc
-run_unittests_SOURCES += message_initializer_unittest.cc
run_unittests_SOURCES += message_initializer_unittest_2.cc
-run_unittests_SOURCES += run_unittests.cc
-run_unittests_SOURCES += log_formatter_unittest.cc
+run_unittests_SOURCES += message_initializer_unittest.cc
+run_unittests_SOURCES += message_reader_unittest.cc
+run_unittests_SOURCES += output_option_unittest.cc
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_CLANGPP
+# This is to workaround unused variables tcout and tcerr in
+# log4cplus's streams.h.
+run_unittests_CXXFLAGS += -Wno-unused-variable
+endif
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
-TESTS += logger_support_test
-logger_support_test_SOURCES = logger_support_test.cc
-logger_support_test_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-logger_support_test_LDFLAGS = $(AM_LDFLAGS)
-logger_support_test_LDADD = $(top_builddir)/src/lib/log/liblog.la
-logger_support_test_LDADD += $(top_builddir)/src/lib/util/libutil.la
+check_PROGRAMS = logger_example
+logger_example_SOURCES = logger_example.cc
+logger_example_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+logger_example_LDFLAGS = $(AM_LDFLAGS) $(LOG4CPLUS_LDFLAGS)
+logger_example_LDADD = $(top_builddir)/src/lib/log/liblog.la
+logger_example_LDADD += $(top_builddir)/src/lib/util/libutil.la
noinst_PROGRAMS = $(TESTS)
# Additional test using the shell
-PYTESTS = run_time_init_test.sh
+PYTESTS = console_test.sh local_file_test.sh severity_test.sh
check-local:
- $(SHELL) $(abs_builddir)/run_time_init_test.sh
+ $(SHELL) $(abs_builddir)/console_test.sh
+ $(SHELL) $(abs_builddir)/destination_test.sh
+ $(SHELL) $(abs_builddir)/local_file_test.sh
+ $(SHELL) $(abs_builddir)/severity_test.sh
diff --git a/src/lib/log/tests/console_test.sh.in b/src/lib/log/tests/console_test.sh.in
new file mode 100755
index 0000000..7ef2684
--- /dev/null
+++ b/src/lib/log/tests/console_test.sh.in
@@ -0,0 +1,69 @@
+#!/bin/sh
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# \brief
+#
+# The logger supports the idea of a "console" logger than logs to either stdout
+# or stderr. This test checks that both these options work.
+
+testname="Console output test"
+echo $testname
+
+failcount=0
+tempfile=@abs_builddir@/console_test_tempfile_$$
+
+# Look at tempfile and check that the count equals the expected count
+passfail() {
+ count=`wc -l $tempfile | awk '{print $1}'`
+ if [ $count -eq $1 ]; then
+ echo " pass"
+ else
+ echo " FAIL"
+ failcount=`expr $failcount + $1`
+ fi
+}
+
+echo -n "1. Checking that console output to stdout goes to stdout:"
+rm -f $tempfile
+./logger_example -c stdout -s error 1> $tempfile 2> /dev/null
+passfail 4
+
+echo -n "2. Checking that console output to stdout does not go to stderr:"
+rm -f $tempfile
+./logger_example -c stdout -s error 1> /dev/null 2> $tempfile
+passfail 0
+
+echo -n "3. Checking that console output to stderr goes to stderr:"
+rm -f $tempfile
+./logger_example -c stderr -s error 1> /dev/null 2> $tempfile
+passfail 4
+
+echo -n "4. Checking that console output to stderr does not go to stdout:"
+rm -f $tempfile
+./logger_example -c stderr -s error 1> $tempfile 2> /dev/null
+passfail 0
+
+if [ $failcount -eq 0 ]; then
+ echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+ echo "FAIL: $testname - 1 test failed"
+else
+ echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up
+rm -f $tempfile
+
+exit $failcount
diff --git a/src/lib/log/tests/destination_test.sh.in b/src/lib/log/tests/destination_test.sh.in
new file mode 100755
index 0000000..ff2d3fb
--- /dev/null
+++ b/src/lib/log/tests/destination_test.sh.in
@@ -0,0 +1,94 @@
+#!/bin/sh
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# \brief Severity test
+#
+# Checks that the logger will limit the output of messages less severy than
+# the severity/debug setting.
+
+testname="Destination test"
+echo $testname
+
+failcount=0
+tempfile=@abs_builddir@/destination_test_tempfile_$$
+destfile1=@abs_builddir@/destination_test_destfile_1_$$
+destfile2=@abs_builddir@/destination_test_destfile_2_$$
+
+passfail() {
+ if [ $1 -eq 0 ]; then
+ echo " pass"
+ else
+ echo " FAIL"
+ failcount=`expr $failcount + $1`
+ fi
+}
+
+echo "1. One logger, multiple destinations:"
+cat > $tempfile << .
+FATAL [example] MSG_WRITERR, error writing to test1: 42
+ERROR [example] MSG_RDLOCMES, reading local message file dummy/file
+FATAL [example.beta] MSG_BADSEVERITY, unrecognized log severity: beta_fatal
+ERROR [example.beta] MSG_BADDESTINATION, unrecognized log destination: beta_error
+.
+rm -f $destfile1 $destfile2
+./logger_example -s error -f $destfile1 -f $destfile2
+
+echo -n " - destination 1:"
+cut -d' ' -f3- $destfile1 | diff $tempfile -
+passfail $?
+
+echo -n " - destination 2:"
+cut -d' ' -f3- $destfile2 | diff $tempfile -
+passfail $?
+
+echo "2. Two loggers, different destinations and severities"
+rm -f $destfile1 $destfile2
+./logger_example -l example -s info -f $destfile1 -l alpha -s warn -f $destfile2
+
+# All output for example and example.beta should have gone to destfile1.
+# Output for example.alpha should have done to destfile2.
+
+cat > $tempfile << .
+FATAL [example] MSG_WRITERR, error writing to test1: 42
+ERROR [example] MSG_RDLOCMES, reading local message file dummy/file
+WARN [example] MSG_BADSTREAM, bad log console output stream: example
+FATAL [example.beta] MSG_BADSEVERITY, unrecognized log severity: beta_fatal
+ERROR [example.beta] MSG_BADDESTINATION, unrecognized log destination: beta_error
+WARN [example.beta] MSG_BADSTREAM, bad log console output stream: beta_warn
+INFO [example.beta] MSG_READERR, error reading from message file beta: info
+.
+echo -n " - destination 1:"
+cut -d' ' -f3- $destfile1 | diff $tempfile -
+passfail $?
+
+echo -n " - destination 2:"
+cat > $tempfile << .
+WARN [example.alpha] MSG_READERR, error reading from message file a.txt: dummy reason
+.
+cut -d' ' -f3- $destfile2 | diff $tempfile -
+passfail $?
+
+if [ $failcount -eq 0 ]; then
+ echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+ echo "FAIL: $testname - 1 test failed"
+else
+ echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up.
+rm -f $tempfile $destfile1 $destfile2
+
+exit $failcount
diff --git a/src/lib/log/tests/local_file_test.sh.in b/src/lib/log/tests/local_file_test.sh.in
new file mode 100755
index 0000000..53b0c2f
--- /dev/null
+++ b/src/lib/log/tests/local_file_test.sh.in
@@ -0,0 +1,86 @@
+#!/bin/sh
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# \brief Local message file test
+#
+# Checks that a local message file can override the definitions in the message
+# dictionary.
+
+testname="Local message file test"
+echo $testname
+
+failcount=0
+localmes=@abs_builddir@/localdef_mes_$$
+tempfile=@abs_builddir@/run_time_init_test_tempfile_$$
+
+passfail() {
+ if [ $1 -eq 0 ]; then
+ echo " pass"
+ else
+ echo " FAIL"
+ failcount=`expr $failcount + $1`
+ fi
+}
+
+# Create the local message file for testing
+
+cat > $localmes << .
+\$PREFIX MSG_
+% NOTHERE this message is not in the global dictionary
+% READERR replacement read error, parameters: '%1' and '%2'
+% RDLOCMES replacement read local message file, parameter is '%1'
+.
+
+echo -n "1. Local message replacement:"
+cat > $tempfile << .
+WARN [example.log] MSG_IDNOTFND, could not replace message text for 'MSG_NOTHERE': no such message
+FATAL [example] MSG_WRITERR, error writing to test1: 42
+ERROR [example] MSG_RDLOCMES, replacement read local message file, parameter is 'dummy/file'
+WARN [example] MSG_BADSTREAM, bad log console output stream: example
+WARN [example.alpha] MSG_READERR, replacement read error, parameters: 'a.txt' and 'dummy reason'
+FATAL [example.beta] MSG_BADSEVERITY, unrecognized log severity: beta_fatal
+ERROR [example.beta] MSG_BADDESTINATION, unrecognized log destination: beta_error
+WARN [example.beta] MSG_BADSTREAM, bad log console output stream: beta_warn
+.
+./logger_example -c stdout -s warn $localmes | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "2. Report error if unable to read local message file:"
+cat > $tempfile << .
+ERROR [example.log] MSG_OPENIN, unable to open message file $localmes for input: No such file or directory
+FATAL [example] MSG_WRITERR, error writing to test1: 42
+ERROR [example] MSG_RDLOCMES, reading local message file dummy/file
+WARN [example] MSG_BADSTREAM, bad log console output stream: example
+WARN [example.alpha] MSG_READERR, error reading from message file a.txt: dummy reason
+FATAL [example.beta] MSG_BADSEVERITY, unrecognized log severity: beta_fatal
+ERROR [example.beta] MSG_BADDESTINATION, unrecognized log destination: beta_error
+WARN [example.beta] MSG_BADSTREAM, bad log console output stream: beta_warn
+.
+rm -f $localmes
+./logger_example -c stdout -s warn $localmes | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+if [ $failcount -eq 0 ]; then
+ echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+ echo "FAIL: $testname - 1 test failed"
+else
+ echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up.
+rm -f $tempfile
+
+exit $failcount
diff --git a/src/lib/log/tests/log_formatter_unittest.cc b/src/lib/log/tests/log_formatter_unittest.cc
index b67831a..b91665d 100644
--- a/src/lib/log/tests/log_formatter_unittest.cc
+++ b/src/lib/log/tests/log_formatter_unittest.cc
@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
#include <log/log_formatter.h>
+#include <log/logger_level.h>
#include <vector>
#include <string>
@@ -24,11 +25,11 @@ namespace {
class FormatterTest : public ::testing::Test {
protected:
- typedef pair<const char*, string> Output;
+ typedef pair<isc::log::Severity, string> Output;
typedef isc::log::Formatter<FormatterTest> Formatter;
vector<Output> outputs;
public:
- void output(const char* prefix, const string& message) {
+ void output(const isc::log::Severity& prefix, const string& message) {
outputs.push_back(Output(prefix, message));
}
// Just shortcut for new string
@@ -46,9 +47,9 @@ TEST_F(FormatterTest, inactive) {
// Create an active formatter and check it produces output. Does no arg
// substitution yet
TEST_F(FormatterTest, active) {
- Formatter("TEST", s("Text of message"), this);
+ Formatter(isc::log::INFO, s("Text of message"), this);
ASSERT_EQ(1, outputs.size());
- EXPECT_STREQ("TEST", outputs[0].first);
+ EXPECT_EQ(isc::log::INFO, outputs[0].first);
EXPECT_EQ("Text of message", outputs[0].second);
}
@@ -62,53 +63,53 @@ TEST_F(FormatterTest, inactiveArg) {
TEST_F(FormatterTest, stringArg) {
{
SCOPED_TRACE("C++ string");
- Formatter("TEST", s("Hello %1"), this).arg(string("World"));
+ Formatter(isc::log::INFO, s("Hello %1"), this).arg(string("World"));
ASSERT_EQ(1, outputs.size());
- EXPECT_STREQ("TEST", outputs[0].first);
+ EXPECT_EQ(isc::log::INFO, outputs[0].first);
EXPECT_EQ("Hello World", outputs[0].second);
}
{
SCOPED_TRACE("C++ string");
- Formatter("TEST", s("Hello %1"), this).arg(string("Internet"));
+ Formatter(isc::log::INFO, s("Hello %1"), this).arg(string("Internet"));
ASSERT_EQ(2, outputs.size());
- EXPECT_STREQ("TEST", outputs[1].first);
+ EXPECT_EQ(isc::log::INFO, outputs[1].first);
EXPECT_EQ("Hello Internet", outputs[1].second);
}
}
// Can convert to string
TEST_F(FormatterTest, intArg) {
- Formatter("TEST", s("The answer is %1"), this).arg(42);
+ Formatter(isc::log::INFO, s("The answer is %1"), this).arg(42);
ASSERT_EQ(1, outputs.size());
- EXPECT_STREQ("TEST", outputs[0].first);
+ EXPECT_EQ(isc::log::INFO, outputs[0].first);
EXPECT_EQ("The answer is 42", outputs[0].second);
}
// Can use multiple arguments at different places
TEST_F(FormatterTest, multiArg) {
- Formatter("TEST", s("The %2 are %1"), this).arg("switched").
+ Formatter(isc::log::INFO, s("The %2 are %1"), this).arg("switched").
arg("arguments");
ASSERT_EQ(1, outputs.size());
- EXPECT_STREQ("TEST", outputs[0].first);
+ EXPECT_EQ(isc::log::INFO, outputs[0].first);
EXPECT_EQ("The arguments are switched", outputs[0].second);
}
// Can survive and complains if placeholder is missing
TEST_F(FormatterTest, missingPlace) {
- EXPECT_NO_THROW(Formatter("TEST", s("Missing the first %2"), this).
+ EXPECT_NO_THROW(Formatter(isc::log::INFO, s("Missing the first %2"), this).
arg("missing").arg("argument"));
ASSERT_EQ(1, outputs.size());
- EXPECT_STREQ("TEST", outputs[0].first);
+ EXPECT_EQ(isc::log::INFO, outputs[0].first);
EXPECT_EQ("Missing the first argument "
"@@Missing placeholder %1 for 'missing'@@", outputs[0].second);
}
// Can replace multiple placeholders
TEST_F(FormatterTest, multiPlaceholder) {
- Formatter("TEST", s("The %1 is the %1"), this).
+ Formatter(isc::log::INFO, s("The %1 is the %1"), this).
arg("first rule of tautology club");
ASSERT_EQ(1, outputs.size());
- EXPECT_STREQ("TEST", outputs[0].first);
+ EXPECT_EQ(isc::log::INFO, outputs[0].first);
EXPECT_EQ("The first rule of tautology club is "
"the first rule of tautology club", outputs[0].second);
}
@@ -116,9 +117,9 @@ TEST_F(FormatterTest, multiPlaceholder) {
// Test we can cope with replacement containing the placeholder
TEST_F(FormatterTest, noRecurse) {
// If we recurse, this will probably eat all the memory and crash
- Formatter("TEST", s("%1"), this).arg("%1 %1");
+ Formatter(isc::log::INFO, s("%1"), this).arg("%1 %1");
ASSERT_EQ(1, outputs.size());
- EXPECT_STREQ("TEST", outputs[0].first);
+ EXPECT_EQ(isc::log::INFO, outputs[0].first);
EXPECT_EQ("%1 %1", outputs[0].second);
}
diff --git a/src/lib/log/tests/logger_example.cc b/src/lib/log/tests/logger_example.cc
new file mode 100644
index 0000000..6b43c18
--- /dev/null
+++ b/src/lib/log/tests/logger_example.cc
@@ -0,0 +1,305 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// \brief Example Program
+///
+/// Simple example program showing how to use the logger. The various
+/// command-line options let most aspects of the logger be exercised, so
+/// making this a useful tool for testing.
+///
+/// See the usage() method for details of use.
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <util/strutil.h>
+
+#include <log/logger.h>
+#include <log/logger_level.h>
+#include <log/logger_manager.h>
+#include <log/logger_name.h>
+#include <log/logger_specification.h>
+#include <log/macros.h>
+
+// Include a set of message definitions.
+#include <log/messagedef.h>
+
+using namespace isc::log;
+using namespace std;
+
+
+// Print usage information
+
+void usage() {
+ cout <<
+"logger_support_test [-h | [logger_spec] [[logger_spec]...]]\n"
+"\n"
+" -h Print this message and exit\n"
+"\n"
+"The rest of the command line comprises the set of logger specifications.\n"
+"Each specification is of the form:\n"
+"\n"
+" -l logger [-s severity] [-d dbglevel] output_spec] [[output_spec] ...\n"
+"\n"
+"where:\n"
+"\n"
+" -l logger Give the name of the logger to which the following\n"
+" output specifications will apply.\n"
+"\n"
+"Each logger is followed by the indication of the serverity it is logging\n"
+"and, if applicable, its debug level:\n"
+"\n"
+" -d dbglevel Debug level. Only interpreted if the severity is 'debug'\n"
+" this is a number between 0 and 99.\n"
+" -s severity Set the severity of messages output. 'severity' is one\n"
+" of 'debug', 'info', 'warn', 'error', 'fatal', the default\n"
+" being 'info'.\n"
+"\n"
+"The output specifications - there may be more than one per logger - detail\n"
+"the output streams attached to the logger. These are of the form:\n"
+"\n"
+" -c stream | -f file [-m maxver] [-z maxsize] | -y facility\n"
+"\n"
+"These are:\n"
+"\n"
+" -c stream Send output to the console. 'stream' is one of 'stdout'\n"
+" of 'stderr'.\n"
+" -f file Send output to specified file, appending to existing file\n"
+" if one exists.\n"
+" -y facility Send output to the syslog file with the given facility\n"
+" name (e.g. local1, cron etc.)\n"
+"\n"
+"The following can be specified for the file logger:\n"
+"\n"
+" -m maxver If file rolling is selected (by the maximum file size being\n"
+" non-zero), the maximum number of versions to keep (defaults\n"
+" to 0)\n"
+" -z maxsize Maximum size of the file before the file is closed and a\n"
+" new one opened. The default of 0 means no maximum size.\n"
+"\n"
+"If none of -c, -f or -y is given, by default, output is sent to stdout. If no\n"
+"logger is specified, the default is the program's root logger ('example').\n";
+
+}
+
+
+// The program sets the attributes on the root logger and logs a set of
+// messages. Looking at the output determines whether the program worked.
+
+int main(int argc, char** argv) {
+ const string ROOT_NAME = "example";
+
+ bool sw_found = false; // Set true if switch found
+ bool c_found = false; // Set true if "-c" found
+ bool f_found = false; // Set true if "-f" found
+ bool y_found = false; // Set true if "-y" found
+ int option; // For getopt() processing
+ OutputOption def_opt; // Default output option - used
+ // for initialization
+ LoggerSpecification cur_spec(ROOT_NAME);// Current specification
+ OutputOption cur_opt; // Current output option
+ vector<LoggerSpecification> loggers; // Set of logger specifications
+ vector<OutputOption> options; // Output options for logger
+ std::string severity; // Severity set for logger
+
+ // Initialize logging system - set the root logger name.
+ LoggerManager manager;
+ manager.init(ROOT_NAME);
+
+ // In the parsing loop that follows, the construction of the logging
+ // specification is always "one behind". In other words, the parsing of
+ // command-line options updates thge current logging specification/output
+ // options. When the flag indicating a new logger or output specification
+ // is encountered, the previous one is added to the list.
+ //
+ // One complication is that there is deemed to be a default active when
+ // the parsing starts (console output for the BIND 10 root logger). This
+ // is included in the logging specifications UNLESS the first switch on
+ // the command line is a "-l" flag starting a new logger. To track this,
+ // the "sw_found" flag is set when a switch is completey processed. The
+ // processing of "-l" will only add information for a previous logger to
+ // the list if this flag is set.
+ while ((option = getopt(argc, argv, "hc:d:f:l:m:s:y:z:")) != -1) {
+ switch (option) {
+ case 'c': // Console output
+ // New output spec. If one was currently active, add it to the
+ // list and reset the current output option to the defaults.
+ if (c_found || f_found || y_found) {
+ cur_spec.addOutputOption(cur_opt);
+ cur_opt = def_opt;
+ c_found = f_found = y_found = false;
+ }
+
+ // Set the output option for this switch.
+ c_found = true;
+ cur_opt.destination = OutputOption::DEST_CONSOLE;
+ if (strcmp(optarg, "stdout") == 0) {
+ cur_opt.stream = OutputOption::STR_STDOUT;
+
+ } else if (strcmp(optarg, "stderr") == 0) {
+ cur_opt.stream = OutputOption::STR_STDERR;
+
+ } else {
+ cerr << "Unrecognised console option: " << optarg << "\n";
+ return (1);
+ }
+ break;
+
+ case 'd': // Debug level
+ cur_spec.setDbglevel(boost::lexical_cast<int>(optarg));
+ break;
+
+ case 'f': // File output specification
+ // New output spec. If one was currently active, add it to the
+ // list and reset the current output option to the defaults.
+ if (c_found || f_found || y_found) {
+ cur_spec.addOutputOption(cur_opt);
+ cur_opt = def_opt;
+ c_found = f_found = y_found = false;
+ }
+
+ // Set the output option for this switch.
+ f_found = true;
+ cur_opt.destination = OutputOption::DEST_FILE;
+ cur_opt.filename = optarg;
+ break;
+
+ case 'h': // Help
+ usage();
+ return (0);
+
+ case 'l': // Logger
+ // If a current specification is active, add the last output option
+ // to it, add it to the list and reset. A specification is active
+ // if at least one switch has been previously found.
+ if (sw_found) {
+ cur_spec.addOutputOption(cur_opt);
+ loggers.push_back(cur_spec);
+ cur_spec.reset();
+ }
+
+ // Set the logger name
+ cur_spec.setName(std::string(optarg));
+
+ // Reset the output option to the default.
+ cur_opt = def_opt;
+
+ // Indicate nothing is found to prevent the console option (the
+ // default output option) being added to the output list if an
+ // output option is found.
+ c_found = f_found = y_found = false;
+ break;
+
+ case 'm': // Maximum file version
+ if (!f_found) {
+ std::cerr << "Attempt to set maximum version (-m) "
+ "outside of file output specification\n";
+ return (1);
+ }
+ try {
+ cur_opt.maxsize = boost::lexical_cast<unsigned int>(optarg);
+ } catch (boost::bad_lexical_cast&) {
+ std::cerr << "Maximum version (-m) argument must be a positive "
+ "integer\n";
+ return (1);
+ }
+ break;
+
+ case 's': // Severity
+ severity = optarg;
+ isc::util::str::uppercase(severity);
+ cur_spec.setSeverity(getSeverity(severity));
+ break;
+
+ case 'y': // Syslog output
+ // New output spec. If one was currently active, add it to the
+ // list and reset the current output option to the defaults.
+ if (c_found || f_found || y_found) {
+ cur_spec.addOutputOption(cur_opt);
+ cur_opt = def_opt;
+ c_found = f_found = y_found = false;
+ }
+ y_found = true;
+ cur_opt.destination = OutputOption::DEST_SYSLOG;
+ cur_opt.facility = optarg;
+ break;
+
+ case 'z': // Maximum size
+ if (! f_found) {
+ std::cerr << "Attempt to set file size (-z) "
+ "outside of file output specification\n";
+ return (1);
+ }
+ try {
+ cur_opt.maxsize = boost::lexical_cast<size_t>(optarg);
+ } catch (boost::bad_lexical_cast&) {
+ std::cerr << "File size (-z) argument must be a positive "
+ "integer\n";
+ return (1);
+ }
+ break;
+
+
+ default:
+ std::cerr << "Unrecognised option: " <<
+ static_cast<char>(option) << "\n";
+ return (1);
+ }
+
+ // Have found at least one command-line switch, so note the fact.
+ sw_found = true;
+ }
+
+ // Add the current (unfinished specification) to the list.
+ cur_spec.addOutputOption(cur_opt);
+ loggers.push_back(cur_spec);
+
+ // Set the logging options.
+ manager.process(loggers.begin(), loggers.end());
+
+ // Set the local file
+ if (optind < argc) {
+ LoggerManager::readLocalMessageFile(argv[optind]);
+ }
+
+ // Log a few messages to different loggers.
+ isc::log::Logger logger_ex(ROOT_NAME);
+ isc::log::Logger logger_alpha("alpha");
+ isc::log::Logger logger_beta("beta");
+
+ LOG_FATAL(logger_ex, MSG_WRITERR).arg("test1").arg("42");
+ LOG_ERROR(logger_ex, MSG_RDLOCMES).arg("dummy/file");
+ LOG_WARN(logger_ex, MSG_BADSTREAM).arg("example");
+ LOG_WARN(logger_alpha, MSG_READERR).arg("a.txt").arg("dummy reason");
+ LOG_INFO(logger_alpha, MSG_OPENIN).arg("example.msg").arg("dummy reason");
+ LOG_DEBUG(logger_ex, 0, MSG_RDLOCMES).arg("example/0");
+ LOG_DEBUG(logger_ex, 24, MSG_RDLOCMES).arg("example/24");
+ LOG_DEBUG(logger_ex, 25, MSG_RDLOCMES).arg("example/25");
+ LOG_DEBUG(logger_ex, 26, MSG_RDLOCMES).arg("example/26");
+ LOG_FATAL(logger_beta, MSG_BADSEVERITY).arg("beta_fatal");
+ LOG_ERROR(logger_beta, MSG_BADDESTINATION).arg("beta_error");
+ LOG_WARN(logger_beta, MSG_BADSTREAM).arg("beta_warn");
+ LOG_INFO(logger_beta, MSG_READERR).arg("beta").arg("info");
+ LOG_DEBUG(logger_beta, 25, MSG_BADSEVERITY).arg("beta/25");
+ LOG_DEBUG(logger_beta, 26, MSG_BADSEVERITY).arg("beta/26");
+
+ return (0);
+}
diff --git a/src/lib/log/tests/logger_impl_log4cxx_unittest.cc b/src/lib/log/tests/logger_impl_log4cxx_unittest.cc
deleted file mode 100644
index cab2678..0000000
--- a/src/lib/log/tests/logger_impl_log4cxx_unittest.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <iostream>
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include <log/root_logger_name.h>
-#include <log/logger.h>
-#include <log/logger_impl.h>
-#include <log/messagedef.h>
-
-using namespace isc;
-using namespace isc::log;
-using namespace std;
-
-/// \brief Log4cxx Implementation Tests
-///
-/// Some tests of methods that are not directly tested by the logger unit tests
-/// (when the logger is configured to use log4cxx)
-
-namespace isc {
-namespace log {
-
-/// \brief Test Logger
-///
-/// This logger is a subclass of the logger implementation class under test, but
-/// makes protected methods public (for testing)
-
-class TestLoggerImpl : public LoggerImpl {
-public:
- /// \brief constructor
- TestLoggerImpl(const string& name) : LoggerImpl(name, true)
- {}
-
-
- /// \brief Conversion Between log4cxx Number and BIND-10 Severity
- Severity convertLevel(int value) {
- return (LoggerImpl::convertLevel(value));
- }
-};
-
-} // namespace log
-} // namespace isc
-
-
-class LoggerImplTest : public ::testing::Test {
-protected:
- LoggerImplTest()
- {
- }
-};
-
-// Test the number to severity conversion function
-
-TEST_F(LoggerImplTest, ConvertLevel) {
-
- // Create a logger
- RootLoggerName::setName("test3");
- TestLoggerImpl logger("alpha");
-
- // Basic 1:1
- EXPECT_EQ(isc::log::DEBUG, logger.convertLevel(log4cxx::Level::DEBUG_INT));
- EXPECT_EQ(isc::log::INFO, logger.convertLevel(log4cxx::Level::INFO_INT));
- EXPECT_EQ(isc::log::WARN, logger.convertLevel(log4cxx::Level::WARN_INT));
- EXPECT_EQ(isc::log::WARN, logger.convertLevel(log4cxx::Level::WARN_INT));
- EXPECT_EQ(isc::log::ERROR, logger.convertLevel(log4cxx::Level::ERROR_INT));
- EXPECT_EQ(isc::log::FATAL, logger.convertLevel(log4cxx::Level::FATAL_INT));
- EXPECT_EQ(isc::log::FATAL, logger.convertLevel(log4cxx::Level::FATAL_INT));
- EXPECT_EQ(isc::log::NONE, logger.convertLevel(log4cxx::Level::OFF_INT));
-
- // Now some debug levels
- EXPECT_EQ(isc::log::DEBUG,
- logger.convertLevel(log4cxx::Level::DEBUG_INT - 1));
- EXPECT_EQ(isc::log::DEBUG,
- logger.convertLevel(log4cxx::Level::DEBUG_INT - MAX_DEBUG_LEVEL));
- EXPECT_EQ(isc::log::DEBUG,
- logger.convertLevel(log4cxx::Level::DEBUG_INT - 2 * MAX_DEBUG_LEVEL));
-}
diff --git a/src/lib/log/tests/logger_level_impl_unittest.cc b/src/lib/log/tests/logger_level_impl_unittest.cc
new file mode 100644
index 0000000..0ded7f9
--- /dev/null
+++ b/src/lib/log/tests/logger_level_impl_unittest.cc
@@ -0,0 +1,171 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <string>
+
+#include <gtest/gtest.h>
+#include <boost/static_assert.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <log/logger_level_impl.h>
+#include <log4cplus/logger.h>
+
+using namespace isc::log;
+using namespace std;
+
+class LoggerLevelImplTest : public ::testing::Test {
+protected:
+ LoggerLevelImplTest()
+ {}
+
+ ~LoggerLevelImplTest()
+ {}
+};
+
+
+// Checks that the log4cplus and BIND 10 levels convert correctly
+TEST_F(LoggerLevelImplTest, DefaultConversionFromBind) {
+ log4cplus::LogLevel fatal =
+ LoggerLevelImpl::convertFromBindLevel(Level(FATAL));
+ EXPECT_EQ(log4cplus::FATAL_LOG_LEVEL, fatal);
+
+ log4cplus::LogLevel error =
+ LoggerLevelImpl::convertFromBindLevel(Level(ERROR));
+ EXPECT_EQ(log4cplus::ERROR_LOG_LEVEL, error);
+
+ log4cplus::LogLevel warn =
+ LoggerLevelImpl::convertFromBindLevel(Level(WARN));
+ EXPECT_EQ(log4cplus::WARN_LOG_LEVEL, warn);
+
+ log4cplus::LogLevel info =
+ LoggerLevelImpl::convertFromBindLevel(Level(INFO));
+ EXPECT_EQ(log4cplus::INFO_LOG_LEVEL, info);
+
+ log4cplus::LogLevel debug =
+ LoggerLevelImpl::convertFromBindLevel(Level(DEBUG));
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL, debug);
+}
+
+// Checks that the debug severity and level converts correctly
+TEST_F(LoggerLevelImplTest, DebugConversionFromBind) {
+ log4cplus::LogLevel debug0 =
+ LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, 0));
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - 0, debug0);
+
+ log4cplus::LogLevel debug1 =
+ LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, 1));
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - 1, debug1);
+
+ log4cplus::LogLevel debug99 =
+ LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, 99));
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - 99, debug99);
+
+ // Out of range should be coerced to the nearest boundary
+ log4cplus::LogLevel debug_1 =
+ LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, MIN_DEBUG_LEVEL - 1));
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL, debug_1);
+
+ log4cplus::LogLevel debug100 =
+ LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, MAX_DEBUG_LEVEL + 1));
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - MAX_DEBUG_LEVEL, debug100);
+}
+
+// Do the checks the other way
+static void
+test_convert_to(const char* trace, isc::log::Severity severity, int dbglevel,
+ log4cplus::LogLevel level)
+{
+ SCOPED_TRACE(trace);
+ Level test = LoggerLevelImpl::convertToBindLevel(level);
+ EXPECT_EQ(severity, test.severity);
+ EXPECT_EQ(dbglevel, test.dbglevel);
+}
+
+TEST_F(LoggerLevelImplTest, ConversionToBind) {
+ test_convert_to("FATAL", FATAL, MIN_DEBUG_LEVEL, log4cplus::FATAL_LOG_LEVEL);
+ test_convert_to("ERROR", ERROR, MIN_DEBUG_LEVEL, log4cplus::ERROR_LOG_LEVEL);
+ test_convert_to("WARN", WARN , MIN_DEBUG_LEVEL, log4cplus::WARN_LOG_LEVEL);
+ test_convert_to("INFO", INFO , MIN_DEBUG_LEVEL, log4cplus::INFO_LOG_LEVEL);
+ test_convert_to("DEBUG", DEBUG, MIN_DEBUG_LEVEL, log4cplus::DEBUG_LOG_LEVEL);
+
+ test_convert_to("DEBUG0", DEBUG, MIN_DEBUG_LEVEL + 0,
+ (log4cplus::DEBUG_LOG_LEVEL));
+ test_convert_to("DEBUG1", DEBUG, MIN_DEBUG_LEVEL + 1,
+ (log4cplus::DEBUG_LOG_LEVEL - 1));
+ test_convert_to("DEBUG2", DEBUG, MIN_DEBUG_LEVEL + 2,
+ (log4cplus::DEBUG_LOG_LEVEL - 2));
+ test_convert_to("DEBUG99", DEBUG, MIN_DEBUG_LEVEL + 99,
+ (log4cplus::DEBUG_LOG_LEVEL - 99));
+
+ // ... and some invalid valid values
+ test_convert_to("DEBUG-1", INFO, MIN_DEBUG_LEVEL,
+ (log4cplus::DEBUG_LOG_LEVEL + 1));
+ BOOST_STATIC_ASSERT(MAX_DEBUG_LEVEL == 99);
+ test_convert_to("DEBUG+100", DEFAULT, 0,
+ (log4cplus::DEBUG_LOG_LEVEL - MAX_DEBUG_LEVEL - 1));
+}
+
+// Check that we can convert from a string to the new log4cplus levels
+TEST_F(LoggerLevelImplTest, FromString) {
+
+ // Test all valid values
+ for (int i = MIN_DEBUG_LEVEL; i <= MAX_DEBUG_LEVEL; ++i) {
+ std::string token = string("DEBUG") + boost::lexical_cast<std::string>(i);
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - i,
+ LoggerLevelImpl::logLevelFromString(token));
+ }
+
+ // ... in lowercase too
+ for (int i = MIN_DEBUG_LEVEL; i <= MAX_DEBUG_LEVEL; ++i) {
+ std::string token = string("debug") + boost::lexical_cast<std::string>(i);
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - i,
+ LoggerLevelImpl::logLevelFromString(token));
+ }
+
+ // A few below the minimum
+ for (int i = MIN_DEBUG_LEVEL - 5; i < MIN_DEBUG_LEVEL; ++i) {
+ std::string token = string("DEBUG") + boost::lexical_cast<std::string>(i);
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL, LoggerLevelImpl::logLevelFromString(token));
+ }
+
+ // ... and above the maximum
+ for (int i = MAX_DEBUG_LEVEL + 1; i < MAX_DEBUG_LEVEL + 5; ++i) {
+ std::string token = string("DEBUG") + boost::lexical_cast<std::string>(i);
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - MAX_DEBUG_LEVEL,
+ LoggerLevelImpl::logLevelFromString(token));
+ }
+
+ // Invalid strings.
+ EXPECT_EQ(log4cplus::NOT_SET_LOG_LEVEL,
+ LoggerLevelImpl::logLevelFromString("DEBU"));
+ EXPECT_EQ(log4cplus::NOT_SET_LOG_LEVEL,
+ LoggerLevelImpl::logLevelFromString("unrecognised"));
+}
+
+// ... and check the conversion back again. All levels should convert to "DEBUG".
+TEST_F(LoggerLevelImplTest, ToString) {
+
+ for (int i = MIN_DEBUG_LEVEL; i <= MAX_DEBUG_LEVEL; ++i) {
+ EXPECT_EQ(std::string("DEBUG"),
+ LoggerLevelImpl::logLevelToString(log4cplus::DEBUG_LOG_LEVEL - i));
+ }
+
+ // ... and that out of range stuff returns an empty string.
+ EXPECT_EQ(std::string(),
+ LoggerLevelImpl::logLevelToString(log4cplus::DEBUG_LOG_LEVEL + 1));
+ EXPECT_EQ(std::string(),
+ LoggerLevelImpl::logLevelToString(
+ log4cplus::DEBUG_LOG_LEVEL - MAX_DEBUG_LEVEL - 100));
+}
diff --git a/src/lib/log/tests/logger_level_unittest.cc b/src/lib/log/tests/logger_level_unittest.cc
new file mode 100644
index 0000000..13d33b2
--- /dev/null
+++ b/src/lib/log/tests/logger_level_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/logger.h>
+#include <log/logger_manager.h>
+#include <log/logger_name.h>
+#include <log/messagedef.h>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+class LoggerLevelTest : public ::testing::Test {
+protected:
+ LoggerLevelTest() {
+ // Logger initialization is done in main()
+ }
+ ~LoggerLevelTest() {
+ LoggerManager::reset();
+ }
+};
+
+
+// Checks that the logger is named correctly.
+
+TEST_F(LoggerLevelTest, Creation) {
+
+ // Default
+ isc::log::Level level1;
+ EXPECT_EQ(isc::log::DEFAULT, level1.severity);
+ EXPECT_EQ(isc::log::MIN_DEBUG_LEVEL, level1.dbglevel);
+
+ // Single argument constructor.
+ isc::log::Level level2(isc::log::FATAL);
+ EXPECT_EQ(isc::log::FATAL, level2.severity);
+ EXPECT_EQ(isc::log::MIN_DEBUG_LEVEL, level2.dbglevel);
+
+ // Two-argument constructor
+ isc::log::Level level3(isc::log::DEBUG, 42);
+ EXPECT_EQ(isc::log::DEBUG, level3.severity);
+ EXPECT_EQ(42, level3.dbglevel);
+}
+
+TEST(LoggerLevel, getSeverity) {
+ EXPECT_EQ(DEBUG, getSeverity("DEBUG"));
+ EXPECT_EQ(DEBUG, getSeverity("debug"));
+ EXPECT_EQ(DEBUG, getSeverity("DeBuG"));
+ EXPECT_EQ(INFO, getSeverity("INFO"));
+ EXPECT_EQ(INFO, getSeverity("info"));
+ EXPECT_EQ(INFO, getSeverity("iNfO"));
+ EXPECT_EQ(WARN, getSeverity("WARN"));
+ EXPECT_EQ(WARN, getSeverity("warn"));
+ EXPECT_EQ(WARN, getSeverity("wARn"));
+ EXPECT_EQ(ERROR, getSeverity("ERROR"));
+ EXPECT_EQ(ERROR, getSeverity("error"));
+ EXPECT_EQ(ERROR, getSeverity("ERRoR"));
+ EXPECT_EQ(FATAL, getSeverity("FATAL"));
+ EXPECT_EQ(FATAL, getSeverity("fatal"));
+ EXPECT_EQ(FATAL, getSeverity("FAtaL"));
+
+ // bad values should default to stdout
+ EXPECT_EQ(INFO, getSeverity("some bad value"));
+ EXPECT_EQ(INFO, getSeverity(""));
+
+ LoggerManager::reset();
+}
diff --git a/src/lib/log/tests/logger_manager_unittest.cc b/src/lib/log/tests/logger_manager_unittest.cc
new file mode 100644
index 0000000..115c928
--- /dev/null
+++ b/src/lib/log/tests/logger_manager_unittest.cc
@@ -0,0 +1,321 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <boost/scoped_array.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <log/macros.h>
+#include <log/messagedef.h>
+#include <log/logger.h>
+#include <log/logger_level.h>
+#include <log/logger_manager.h>
+#include <log/logger_specification.h>
+#include <log/output_option.h>
+
+#include "tempdir.h"
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+/// \brief LoggerManager Test
+class LoggerManagerTest : public ::testing::Test {
+public:
+ LoggerManagerTest() {
+ // Initialization of logging is done in main()
+ }
+
+ ~LoggerManagerTest() {
+ LoggerManager::reset();
+ }
+};
+
+
+
+// Convenience class to create the specification for the logger "filelogger",
+// which, as the name suggests, logs to a file. It remembers the file name and
+// deletes the file when instance of the class is destroyed.
+class SpecificationForFileLogger {
+public:
+
+ // Constructor - allocate file and create the specification object
+ SpecificationForFileLogger() : spec_(), name_(""), logname_("filelogger") {
+
+ // Set the output to a temporary file.
+ OutputOption option;
+ option.destination = OutputOption::DEST_FILE;
+ option.filename = name_ = createTempFilename();
+
+ // Set target output to the file logger. The defauls indicate
+ // INFO severity.
+ spec_.setName(logname_);
+ spec_.addOutputOption(option);
+ }
+
+ // Destructor, remove the file. This is only a test, so ignore failures
+ ~SpecificationForFileLogger() {
+ if (! name_.empty()) {
+ (void) unlink(name_.c_str());
+ }
+ }
+
+ // Return reference to the logging specification for this loggger
+ LoggerSpecification& getSpecification() {
+ return spec_;
+ }
+
+ // Return name of the logger
+ string getLoggerName() const {
+ return logname_;
+ }
+
+ // Return name of the file
+ string getFileName() const {
+ return name_;
+ }
+
+ // Create temporary filename
+ //
+ // The compiler warns against tmpnam() and suggests mkstemp instead.
+ // Unfortunately, this creates the filename and opens it. So we need to
+ // close and delete the file before returning the name. Also, the name
+ // is based on the template supplied and the name of the temporary
+ // directory may vary between systems. So translate TMPDIR and if that
+ // does not exist, use /tmp.
+ //
+ // \return Temporary file name
+ std::string createTempFilename() {
+ string filename = TEMP_DIR + "/bind10_logger_manager_test_XXXXXX";
+
+ // Copy into writeable storage for the call to mkstemp
+ boost::scoped_array<char> tname(new char[filename.size() + 1]);
+ strcpy(tname.get(), filename.c_str());
+
+ // Create file, close and delete it, and store the name for later.
+ // There is still a race condition here, albeit a small one.
+ int filenum = mkstemp(tname.get());
+ if (filenum == -1) {
+ isc_throw(Exception, "Unable to obtain unique filename");
+ }
+ close(filenum);
+
+ return (string(tname.get()));
+ }
+
+
+private:
+ LoggerSpecification spec_; // Specification for this file logger
+ string name_; // Name of the output file
+ string logname_; // Name of this logger
+};
+
+
+// Convenience function to read an output log file and check that each line
+// contains the expected message ID
+//
+// \param filename Name of the file to check
+// \param start Iterator pointing to first expected message ID
+// \param finish Iterator pointing to last expected message ID
+template <typename T>
+void checkFileContents(const std::string& filename, T start, T finish) {
+
+ // Access the file for input
+ ifstream infile(filename.c_str());
+ if (! infile.good()) {
+ FAIL() << "Unable to open the logging file " << filename;
+ }
+
+ // Iterate round the expected message IDs and check that they appear in
+ // the string.
+ string line; // Line read from the file
+
+ T i = start; // Iterator
+ getline(infile, line);
+ int lineno = 1;
+
+ while ((i != finish) && (infile.good())) {
+
+ // Check that the message ID appears in the line.
+ EXPECT_TRUE(line.find(string(*i)) != string::npos)
+ << "Expected to find " << string(*i) << " on line " << lineno
+ << " of logging file " << filename;
+
+ // Go for the next line
+ ++i;
+ getline(infile, line);
+ ++lineno;
+ }
+
+ // Why did the loop end?
+ EXPECT_TRUE(i == finish) << "Did not reach the end of the message ID list";
+ EXPECT_TRUE(infile.eof()) << "Did not reach the end of the logging file";
+
+ // File will close when the instream is deleted at the end of this
+ // function.
+}
+
+// Check that the logger correctly creates something logging to a file.
+TEST_F(LoggerManagerTest, FileLogger) {
+
+ // Create a specification for the file logger and use the manager to
+ // connect the "filelogger" logger to it.
+ SpecificationForFileLogger file_spec;
+
+ // For the first test, we want to check that the file is created
+ // if it does not already exist. So delete the temporary file before
+ // logging the first message.
+ unlink(file_spec.getFileName().c_str());
+
+ // Set up the file appenders.
+ LoggerManager manager;
+ manager.process(file_spec.getSpecification());
+
+ // Try logging to the file. Local scope is set to ensure that the logger
+ // is destroyed before we reset the global logging. We record what we
+ // put in the file for a later comparison.
+ vector<MessageID> ids;
+ {
+
+ // Scope-limit the logger to ensure it is destroyed after the brief
+ // check. This adds weight to the idea that the logger will not
+ // keep the file open.
+ Logger logger(file_spec.getLoggerName());
+
+ LOG_FATAL(logger, MSG_DUPMSGID).arg("test");
+ ids.push_back(MSG_DUPMSGID);
+
+ LOG_FATAL(logger, MSG_DUPLNS).arg("test");
+ ids.push_back(MSG_DUPLNS);
+ }
+ LoggerManager::reset();
+
+ // At this point, the output file should contain two lines with messages
+ // MSG_DUPMSGID and MSG_DUPLNS messages - test this.
+ checkFileContents(file_spec.getFileName(), ids.begin(), ids.end());
+
+ // Re-open the file (we have to assume that it was closed when we
+ // reset the logger - there is no easy way to check) and check that
+ // new messages are appended to it. We use the alternative
+ // invocation of process() here to check it works.
+ vector<LoggerSpecification> spec(1, file_spec.getSpecification());
+ manager.process(spec.begin(), spec.end());
+
+ // Create a new instance of the logger and log three more messages.
+ Logger logger(file_spec.getLoggerName());
+
+ LOG_FATAL(logger, MSG_IDNOTFND).arg("test");
+ ids.push_back(MSG_IDNOTFND);
+
+ LOG_FATAL(logger, MSG_INVMSGID).arg("test").arg("test2");
+ ids.push_back(MSG_INVMSGID);
+
+ LOG_FATAL(logger, MSG_NOMSGID).arg("42");
+ ids.push_back(MSG_NOMSGID);
+
+ // Close the file and check again
+ LoggerManager::reset();
+ checkFileContents(file_spec.getFileName(), ids.begin(), ids.end());
+}
+
+// Check if the file rolls over when it gets above a certain size.
+TEST_F(LoggerManagerTest, FileSizeRollover) {
+ // Set to a suitable minimum that log4cplus can copy with
+ static const size_t SIZE_LIMIT = 204800;
+
+ // Set up the name of the file.
+ SpecificationForFileLogger file_spec;
+ LoggerSpecification& spec = file_spec.getSpecification();
+
+ // Expand the option to ensure that a maximum version size is set.
+ LoggerSpecification::iterator opt = spec.begin();
+ EXPECT_TRUE(opt != spec.end());
+ opt->maxsize = SIZE_LIMIT; // Bytes
+ opt->maxver = 2;
+
+ // The current current output file does not exist (the creation of file_spec
+ // ensures that. Check that previous versions don't either.
+ vector<string> prev_name;
+ for (int i = 0; i < 3; ++i) {
+ prev_name.push_back(file_spec.getFileName() + "." +
+ boost::lexical_cast<string>(i + 1));
+ (void) unlink(prev_name[i].c_str());
+ }
+
+ // Generate an argument for a message that ensures that the message when
+ // logged will be over that size.
+ string big_arg(SIZE_LIMIT, 'x');
+
+ // Set up the file logger
+ LoggerManager manager;
+ manager.process(spec);
+
+ // Log the message twice using different message IDs. This should generate
+ // three files as for the log4cplus implementation, the files appear to
+ // be rolled after the message is logged.
+ {
+ Logger logger(file_spec.getLoggerName());
+ LOG_FATAL(logger, MSG_IDNOTFND).arg(big_arg);
+ LOG_FATAL(logger, MSG_DUPLNS).arg(big_arg);
+ }
+
+ // Check them.
+ LoggerManager::reset(); // Ensure files are closed
+
+ vector<MessageID> ids;
+ ids.push_back(MSG_IDNOTFND);
+ checkFileContents(prev_name[1], ids.begin(), ids.end());
+
+ ids.clear();
+ ids.push_back(MSG_DUPLNS);
+ checkFileContents(prev_name[0], ids.begin(), ids.end());
+
+ // Log another message and check that the files have rotated and that
+ // a .3 version does not exist.
+ manager.process(spec);
+ {
+ Logger logger(file_spec.getLoggerName());
+ LOG_FATAL(logger, MSG_NOMSGTXT).arg(big_arg);
+ }
+
+ LoggerManager::reset(); // Ensure files are closed
+
+ // Check that the files have moved.
+ ids.clear();
+ ids.push_back(MSG_DUPLNS);
+ checkFileContents(prev_name[1], ids.begin(), ids.end());
+
+ ids.clear();
+ ids.push_back(MSG_NOMSGTXT);
+ checkFileContents(prev_name[0], ids.begin(), ids.end());
+
+ // ... and check that the .3 version does not exist.
+ ifstream file3(prev_name[2].c_str(), ifstream::in);
+ EXPECT_FALSE(file3.good());
+
+ // Tidy up
+ for (int i = 0; i < prev_name.size(); ++i) {
+ (void) unlink(prev_name[i].c_str());
+ }
+}
diff --git a/src/lib/log/tests/logger_name_unittest.cc b/src/lib/log/tests/logger_name_unittest.cc
new file mode 100644
index 0000000..51fead5
--- /dev/null
+++ b/src/lib/log/tests/logger_name_unittest.cc
@@ -0,0 +1,77 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/logger_name.h>
+
+using namespace isc;
+using namespace isc::log;
+
+// Test class. To avoid disturbing the root logger configuration in other
+// tests in the suite, the root logger name is saved in the constructor and
+// restored in the destructor. However, this is a bit chicken and egg, as the
+// functions used to do the save and restore are those being tested...
+//
+// Note that the root name is originally set by the initialization of the
+// logging configuration done in main().
+
+class LoggerNameTest : public ::testing::Test {
+public:
+ LoggerNameTest() {
+ name_ = getRootLoggerName();
+ }
+ ~LoggerNameTest() {
+ setRootLoggerName(name_);
+ }
+
+private:
+ std::string name_; ///< Saved name
+};
+
+// Check setting and getting of root name
+
+TEST_F(LoggerNameTest, RootNameSetGet) {
+ const std::string name1 = "test1";
+ const std::string name2 = "test2";
+
+ // Check that Set/Get works
+ setRootLoggerName(name1);
+ EXPECT_EQ(name1, getRootLoggerName());
+
+ // We could not test that the root logger name is initialised
+ // correctly (as there is one instance of it and we don't know
+ // when this test will be run) so to check that setName() actually
+ // does change the name, run the test again with a different name.
+ //
+ // (There was always the outside chance that the root logger name
+ // was initialised with name1 and that setName() has no effect.)
+ setRootLoggerName(name2);
+ EXPECT_EQ(name2, getRootLoggerName());
+}
+
+// Check expansion of name
+
+TEST_F(LoggerNameTest, ExpandLoggerName) {
+ const std::string ROOT = "example";
+ const std::string NAME = "something";
+ const std::string FULL_NAME = ROOT + "." + NAME;
+
+ setRootLoggerName(ROOT);
+ EXPECT_EQ(ROOT, expandLoggerName(ROOT));
+ EXPECT_EQ(FULL_NAME, expandLoggerName(NAME));
+ EXPECT_EQ(FULL_NAME, expandLoggerName(FULL_NAME));
+}
diff --git a/src/lib/log/tests/logger_specification_unittest.cc b/src/lib/log/tests/logger_specification_unittest.cc
new file mode 100644
index 0000000..e416c32
--- /dev/null
+++ b/src/lib/log/tests/logger_specification_unittest.cc
@@ -0,0 +1,96 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/logger_specification.h>
+#include <log/output_option.h>
+
+using namespace isc::log;
+using namespace std;
+
+// Check default initialization.
+TEST(LoggerSpecificationTest, DefaultInitialization) {
+ LoggerSpecification spec;
+
+ EXPECT_EQ(string(""), spec.getName());
+ EXPECT_EQ(isc::log::INFO, spec.getSeverity());
+ EXPECT_EQ(0, spec.getDbglevel());
+ EXPECT_FALSE(spec.getAdditive());
+ EXPECT_EQ(0, spec.optionCount());
+}
+
+// Non-default initialization
+TEST(LoggerSpecificationTest, Initialization) {
+ LoggerSpecification spec("alpha", isc::log::ERROR, 42, true);
+
+ EXPECT_EQ(string("alpha"), spec.getName());
+ EXPECT_EQ(isc::log::ERROR, spec.getSeverity());
+ EXPECT_EQ(42, spec.getDbglevel());
+ EXPECT_TRUE(spec.getAdditive());
+ EXPECT_EQ(0, spec.optionCount());
+}
+
+// Get/Set tests
+TEST(LoggerSpecificationTest, SetGet) {
+ LoggerSpecification spec;
+
+ spec.setName("gamma");
+ EXPECT_EQ(string("gamma"), spec.getName());
+
+ spec.setSeverity(isc::log::FATAL);
+ EXPECT_EQ(isc::log::FATAL, spec.getSeverity());
+
+ spec.setDbglevel(7);
+ EXPECT_EQ(7, spec.getDbglevel());
+
+ spec.setAdditive(true);
+ EXPECT_TRUE(spec.getAdditive());
+
+ // Should not affect option count
+ EXPECT_EQ(0, spec.optionCount());
+}
+
+// Check option setting
+TEST(LoggerSpecificationTest, AddOption) {
+ OutputOption option1;
+ option1.destination = OutputOption::DEST_FILE;
+ option1.filename = "/tmp/example.log";
+ option1.maxsize = 123456;
+
+ OutputOption option2;
+ option2.destination = OutputOption::DEST_SYSLOG;
+ option2.facility = "LOCAL7";
+
+ LoggerSpecification spec;
+ spec.addOutputOption(option1);
+ spec.addOutputOption(option2);
+ EXPECT_EQ(2, spec.optionCount());
+
+ // Iterate through them
+ LoggerSpecification::const_iterator i = spec.begin();
+
+ EXPECT_EQ(OutputOption::DEST_FILE, i->destination);
+ EXPECT_EQ(string("/tmp/example.log"), i->filename);
+ EXPECT_EQ(123456, i->maxsize);
+
+ ++i;
+ EXPECT_EQ(OutputOption::DEST_SYSLOG, i->destination);
+ EXPECT_EQ(string("LOCAL7"), i->facility);
+
+ ++i;
+ EXPECT_TRUE(i == spec.end());
+}
diff --git a/src/lib/log/tests/logger_support_test.cc b/src/lib/log/tests/logger_support_test.cc
deleted file mode 100644
index 0a2338b..0000000
--- a/src/lib/log/tests/logger_support_test.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-/// \brief Example Program
-///
-/// Simple example program showing how to use the logger.
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-#include <iostream>
-
-#include <log/logger.h>
-#include <log/macros.h>
-#include <log/logger_support.h>
-#include <log/root_logger_name.h>
-
-// Include a set of message definitions.
-#include <log/messagedef.h>
-
-using namespace isc::log;
-
-// Declare logger to use an example.
-Logger logger_ex("example");
-
-// The program is invoked:
-//
-// logger_support_test [-s severity] [-d level ] [local_file]
-//
-// "severity" is one of "debug", "info", "warn", "error", "fatal"
-// "level" is the debug level, a number between 0 and 99
-// "local_file" is the name of a local file.
-//
-// The program sets the attributes on the root logger and logs a set of
-// messages. Looking at the output determines whether the program worked.
-
-int main(int argc, char** argv) {
-
- isc::log::Severity severity = isc::log::INFO; // Default logger severity
- int dbglevel = -1; // Logger debug level
- const char* localfile = NULL; // Local message file
- int option; // For getopt() processing
- Logger logger_dlm("dlm", true); // Another example logger
-
- // Parse options
- while ((option = getopt(argc, argv, "s:d:")) != -1) {
- switch (option) {
- case 's':
- if (strcmp(optarg, "debug") == 0) {
- severity = isc::log::DEBUG;
- } else if (strcmp(optarg, "info") == 0) {
- severity = isc::log::INFO;
- } else if (strcmp(optarg, "warn") == 0) {
- severity = isc::log::WARN;
- } else if (strcmp(optarg, "error") == 0) {
- severity = isc::log::ERROR;
- } else if (strcmp(optarg, "fatal") == 0) {
- severity = isc::log::FATAL;
- } else {
- std::cout << "Unrecognised severity option: " <<
- optarg << "\n";
- exit(1);
- }
- break;
-
- case 'd':
- dbglevel = atoi(optarg);
- break;
-
- default:
- std::cout << "Unrecognised option: " <<
- static_cast<char>(option) << "\n";
- }
- }
-
- if (optind < argc) {
- localfile = argv[optind];
- }
-
- // Update the logging parameters
- initLogger("alpha", severity, dbglevel, localfile);
-
- // Log a few messages
- LOG_FATAL(logger_ex, MSG_WRITERR).arg("test1").arg("42");
- LOG_ERROR(logger_ex, MSG_RDLOCMES).arg("dummy/file");
- LOG_WARN(logger_dlm, MSG_READERR).arg("a.txt").arg("dummy reason");
- LOG_INFO(logger_dlm, MSG_OPENIN).arg("example.msg").arg("dummy reason");
- LOG_DEBUG(logger_ex, 0, MSG_RDLOCMES).arg("dummy/0");
- LOG_DEBUG(logger_ex, 24, MSG_RDLOCMES).arg("dummy/24");
- LOG_DEBUG(logger_ex, 25, MSG_RDLOCMES).arg("dummy/25");
- LOG_DEBUG(logger_ex, 26, MSG_RDLOCMES).arg("dummy/26");
-
- return (0);
-}
diff --git a/src/lib/log/tests/logger_unittest.cc b/src/lib/log/tests/logger_unittest.cc
index 4eff622..b7858e5 100644
--- a/src/lib/log/tests/logger_unittest.cc
+++ b/src/lib/log/tests/logger_unittest.cc
@@ -17,45 +17,27 @@
#include <gtest/gtest.h>
-#include <log/root_logger_name.h>
#include <log/logger.h>
+#include <log/logger_manager.h>
+#include <log/logger_name.h>
#include <log/messagedef.h>
using namespace isc;
using namespace isc::log;
using namespace std;
-namespace isc {
-namespace log {
-
-/// \brief Test Logger
+/// \brief Logger Test
///
-/// This logger is a subclass of the logger class under test, but makes
-/// protected methods public (for testing)
-
-class TestLogger : public Logger {
-public:
- /// \brief constructor
- TestLogger(const string& name) : Logger(name, true)
- {}
-
- static void reset() {
- Logger::reset();
- }
-};
-
-} // namespace log
-} // namespace isc
-
+/// As the logger is only a shell around the implementation, this tests also
+/// checks the logger implementation class as well.
class LoggerTest : public ::testing::Test {
-protected:
- LoggerTest()
- {
+public:
+ LoggerTest() {
+ // Initialization of logging is done in main()
}
-
~LoggerTest() {
- TestLogger::reset();
+ LoggerManager::reset();
}
};
@@ -65,11 +47,10 @@ protected:
TEST_F(LoggerTest, Name) {
// Create a logger
- setRootLoggerName("test1");
Logger logger("alpha");
// ... and check the name
- EXPECT_EQ(string("test1.alpha"), logger.getName());
+ EXPECT_EQ(getRootLoggerName() + string(".alpha"), logger.getName());
}
// This test attempts to get two instances of a logger with the same name
@@ -77,22 +58,18 @@ TEST_F(LoggerTest, Name) {
TEST_F(LoggerTest, GetLogger) {
- // Set the root logger name (not strictly needed, but this will be the
- // case in the program(.
- setRootLoggerName("test2");
-
const string name1 = "alpha";
const string name2 = "beta";
// Instantiate two loggers that should be the same
- TestLogger logger1(name1);
- TestLogger logger2(name1);
+ Logger logger1(name1);
+ Logger logger2(name1);
// And check they equal
EXPECT_TRUE(logger1 == logger2);
// Instantiate another logger with another name and check that it
// is different to the previously instantiated ones.
- TestLogger logger3(name2);
+ Logger logger3(name2);
EXPECT_FALSE(logger1 == logger3);
}
@@ -101,8 +78,7 @@ TEST_F(LoggerTest, GetLogger) {
TEST_F(LoggerTest, Severity) {
// Create a logger
- setRootLoggerName("test3");
- TestLogger logger("alpha");
+ Logger logger("alpha");
// Now check the levels
logger.setSeverity(isc::log::NONE);
@@ -132,8 +108,7 @@ TEST_F(LoggerTest, Severity) {
TEST_F(LoggerTest, DebugLevels) {
// Create a logger
- setRootLoggerName("test4");
- TestLogger logger("alpha");
+ Logger logger("alpha");
// Debug level should be 0 if not at debug severity
logger.setSeverity(isc::log::NONE, 20);
@@ -174,13 +149,47 @@ TEST_F(LoggerTest, DebugLevels) {
TEST_F(LoggerTest, SeverityInheritance) {
- // Create to loggers. We cheat here as we know that the underlying
- // implementation (in this case log4cxx) will set a parent-child
- // relationship if the loggers are named <parent> and <parent>.<child>.
+ // Create two loggers. We cheat here as we know that the underlying
+ // implementation will set a parent-child relationship if the loggers
+ // are named <parent> and <parent>.<child>.
+ Logger parent("alpha");
+ Logger child("alpha.beta");
- setRootLoggerName("test5");
- TestLogger parent("alpha");
- TestLogger child("alpha.beta");
+ // By default, newly created loggers should have a level of DEFAULT
+ // (i.e. default to parent)
+ EXPECT_EQ(isc::log::DEFAULT, parent.getSeverity());
+ EXPECT_EQ(isc::log::DEFAULT, child.getSeverity());
+
+ // Set the severity of the parent to debug and check what is
+ // reported by the child.
+ parent.setSeverity(isc::log::DEBUG, 42);
+ EXPECT_EQ(42, parent.getDebugLevel());
+ EXPECT_EQ(0, child.getDebugLevel());
+ EXPECT_EQ(42, child.getEffectiveDebugLevel());
+
+ // Setting the child to DEBUG severity should set its own
+ // debug level.
+ child.setSeverity(isc::log::DEBUG, 53);
+ EXPECT_EQ(53, child.getDebugLevel());
+ EXPECT_EQ(53, child.getEffectiveDebugLevel());
+
+ // If the child severity is set to something other than DEBUG,
+ // the debug level should be reported as 0.
+ child.setSeverity(isc::log::ERROR);
+ EXPECT_EQ(0, child.getDebugLevel());
+ EXPECT_EQ(0, child.getEffectiveDebugLevel());
+}
+
+// Check that changing the parent and child debug level does not affect
+// the other.
+
+TEST_F(LoggerTest, DebugLevelInheritance) {
+
+ // Create two loggers. We cheat here as we know that the underlying
+ // implementation will set a parent-child relationship if the loggers
+ // are named <parent> and <parent>.<child>.
+ Logger parent("alpha");
+ Logger child("alpha.beta");
// By default, newly created loggers should have a level of DEFAULT
// (i.e. default to parent)
@@ -206,11 +215,9 @@ TEST_F(LoggerTest, SeverityInheritance) {
TEST_F(LoggerTest, EffectiveSeverityInheritance) {
- // Create to loggers. We cheat here as we know that the underlying
- // implementation (in this case log4cxx) will set a parent-child
- // relationship if the loggers are named <parent> and <parent>.<child>.
-
- setRootLoggerName("test6");
+ // Create two loggers. We cheat here as we know that the underlying
+ // implementation will set a parent-child relationship if the loggers
+ // are named <parent> and <parent>.<child>.
Logger parent("test6");
Logger child("test6.beta");
@@ -245,7 +252,6 @@ TEST_F(LoggerTest, EffectiveSeverityInheritance) {
TEST_F(LoggerTest, IsXxxEnabled) {
- setRootLoggerName("test7");
Logger logger("test7");
logger.setSeverity(isc::log::INFO);
@@ -316,7 +322,6 @@ TEST_F(LoggerTest, IsXxxEnabled) {
TEST_F(LoggerTest, IsDebugEnabledLevel) {
- setRootLoggerName("test8");
Logger logger("test8");
int MID_LEVEL = (MIN_DEBUG_LEVEL + MAX_DEBUG_LEVEL) / 2;
diff --git a/src/lib/log/tests/output_option_unittest.cc b/src/lib/log/tests/output_option_unittest.cc
new file mode 100644
index 0000000..8f0e0de
--- /dev/null
+++ b/src/lib/log/tests/output_option_unittest.cc
@@ -0,0 +1,66 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/output_option.h>
+
+using namespace isc::log;
+using namespace std;
+
+// As OutputOption is a struct, the only meaningful test is to check that it
+// initializes correctly.
+
+TEST(OutputOptionTest, Initialization) {
+ OutputOption option;
+
+ EXPECT_EQ(OutputOption::DEST_CONSOLE, option.destination);
+ EXPECT_EQ(OutputOption::STR_STDERR, option.stream);
+ EXPECT_FALSE(option.flush);
+ EXPECT_EQ(string("LOCAL0"), option.facility);
+ EXPECT_EQ(string(""), option.filename);
+ EXPECT_EQ(0, option.maxsize);
+ EXPECT_EQ(0, option.maxver);
+}
+
+TEST(OutputOption, getDestination) {
+ EXPECT_EQ(OutputOption::DEST_CONSOLE, getDestination("console"));
+ EXPECT_EQ(OutputOption::DEST_CONSOLE, getDestination("CONSOLE"));
+ EXPECT_EQ(OutputOption::DEST_CONSOLE, getDestination("CoNSoLE"));
+ EXPECT_EQ(OutputOption::DEST_FILE, getDestination("file"));
+ EXPECT_EQ(OutputOption::DEST_FILE, getDestination("FILE"));
+ EXPECT_EQ(OutputOption::DEST_FILE, getDestination("fIlE"));
+ EXPECT_EQ(OutputOption::DEST_SYSLOG, getDestination("syslog"));
+ EXPECT_EQ(OutputOption::DEST_SYSLOG, getDestination("SYSLOG"));
+ EXPECT_EQ(OutputOption::DEST_SYSLOG, getDestination("SYSlog"));
+
+ // bad values should default to DEST_CONSOLE
+ EXPECT_EQ(OutputOption::DEST_CONSOLE, getDestination("SOME_BAD_VALUE"));
+}
+
+TEST(OutputOption, getStream) {
+ EXPECT_EQ(OutputOption::STR_STDOUT, getStream("stdout"));
+ EXPECT_EQ(OutputOption::STR_STDOUT, getStream("STDOUT"));
+ EXPECT_EQ(OutputOption::STR_STDOUT, getStream("STdouT"));
+ EXPECT_EQ(OutputOption::STR_STDERR, getStream("stderr"));
+ EXPECT_EQ(OutputOption::STR_STDERR, getStream("STDERR"));
+ EXPECT_EQ(OutputOption::STR_STDERR, getStream("StDeRR"));
+
+ // bad values should default to stdout
+ EXPECT_EQ(OutputOption::STR_STDOUT, getStream("some bad value"));
+ EXPECT_EQ(OutputOption::STR_STDOUT, getStream(""));
+}
+
diff --git a/src/lib/log/tests/root_logger_name_unittest.cc b/src/lib/log/tests/root_logger_name_unittest.cc
deleted file mode 100644
index 8665794..0000000
--- a/src/lib/log/tests/root_logger_name_unittest.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include <log/root_logger_name.h>
-
-using namespace isc;
-using namespace isc::log;
-
-class RootLoggerNameTest : public ::testing::Test {
-protected:
- RootLoggerNameTest()
- {
- }
-};
-
-// Check of the (only) functionality of the class.
-
-TEST_F(RootLoggerNameTest, SetGet) {
- const std::string name1 = "test1";
- const std::string name2 = "test2";
-
- // Check that Set/Get works
- setRootLoggerName(name1);
- EXPECT_EQ(name1, getRootLoggerName());
-
- // We could not test that the root logger name is initialised
- // correctly (as there is one instance of it and we don't know
- // when this test will be run) so to check that setName() actually
- // does change the name, run the test again with a different name.
- //
- // (There was always the outside chance that the root logger name
- // was initialised with name1 and that setName() has no effect.)
- setRootLoggerName(name2);
- EXPECT_EQ(name2, getRootLoggerName());
-}
diff --git a/src/lib/log/tests/run_time_init_test.sh.in b/src/lib/log/tests/run_time_init_test.sh.in
deleted file mode 100755
index e48a781..0000000
--- a/src/lib/log/tests/run_time_init_test.sh.in
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/bin/sh
-# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-failcount=0
-localmes=@abs_builddir@/localdef_mes_$$
-tempfile=@abs_builddir@/run_time_init_test_tempfile_$$
-
-passfail() {
- if [ $1 -eq 0 ]; then
- echo "pass"
- else
- echo "FAIL"
- fi
- failcount=`expr $failcount + $1`
-}
-
-# Create the local message file for testing
-
-cat > $localmes << .
-\$PREFIX MSG_
-% NOTHERE this message is not in the global dictionary
-% READERR replacement read error, parameters: '%1' and '%2'
-% RDLOCMES replacement read local message file, parameter is '%1'
-.
-
-echo -n "1. runInitTest default parameters: "
-cat > $tempfile << .
-FATAL [alpha.example] MSG_WRITERR, error writing to test1: 42
-ERROR [alpha.example] MSG_RDLOCMES, reading local message file dummy/file
-WARN [alpha.dlm] MSG_READERR, error reading from message file a.txt: dummy reason
-INFO [alpha.dlm] MSG_OPENIN, unable to open message file example.msg for input: dummy reason
-.
-./logger_support_test | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-echo -n "2. Severity filter: "
-cat > $tempfile << .
-FATAL [alpha.example] MSG_WRITERR, error writing to test1: 42
-ERROR [alpha.example] MSG_RDLOCMES, reading local message file dummy/file
-.
-./logger_support_test -s error | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-echo -n "3. Debug level: "
-cat > $tempfile << .
-FATAL [alpha.example] MSG_WRITERR, error writing to test1: 42
-ERROR [alpha.example] MSG_RDLOCMES, reading local message file dummy/file
-WARN [alpha.dlm] MSG_READERR, error reading from message file a.txt: dummy reason
-INFO [alpha.dlm] MSG_OPENIN, unable to open message file example.msg for input: dummy reason
-DEBUG [alpha.example] MSG_RDLOCMES, reading local message file dummy/0
-DEBUG [alpha.example] MSG_RDLOCMES, reading local message file dummy/24
-DEBUG [alpha.example] MSG_RDLOCMES, reading local message file dummy/25
-.
-./logger_support_test -s debug -d 25 | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-echo -n "4. Local message replacement: "
-cat > $tempfile << .
-WARN [alpha.log] MSG_IDNOTFND, could not replace message text for 'MSG_NOTHERE': no such message
-FATAL [alpha.example] MSG_WRITERR, error writing to test1: 42
-ERROR [alpha.example] MSG_RDLOCMES, replacement read local message file, parameter is 'dummy/file'
-WARN [alpha.dlm] MSG_READERR, replacement read error, parameters: 'a.txt' and 'dummy reason'
-.
-./logger_support_test -s warn $localmes | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-rm -f $localmes
-rm -f $tempfile
-
-if [ $failcount -eq 0 ]; then
- echo "PASS: run_time_init_test"
-elif [ $failcount -eq 1 ]; then
- echo "FAIL: run_time_init_test - 1 test failed"
-else
- echo "FAIL: run_time_init_test - $failcount tests failed"
-fi
-
-exit $failcount
diff --git a/src/lib/log/tests/run_unittests.cc b/src/lib/log/tests/run_unittests.cc
index bd3c4c9..8a9d1e5 100644
--- a/src/lib/log/tests/run_unittests.cc
+++ b/src/lib/log/tests/run_unittests.cc
@@ -13,9 +13,13 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+
+#include <log/logger_support.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ isc::log::initLogger();
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/log/tests/severity_test.sh.in b/src/lib/log/tests/severity_test.sh.in
new file mode 100755
index 0000000..6f4d27a
--- /dev/null
+++ b/src/lib/log/tests/severity_test.sh.in
@@ -0,0 +1,91 @@
+#!/bin/sh
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# \brief Severity test
+#
+# Checks that the logger will limit the output of messages less severy than
+# the severity/debug setting.
+
+testname="Severity test"
+echo $testname
+
+failcount=0
+tempfile=@abs_builddir@/severity_test_tempfile_$$
+
+passfail() {
+ if [ $1 -eq 0 ]; then
+ echo " pass"
+ else
+ echo " FAIL"
+ failcount=`expr $failcount + $1`
+ fi
+}
+
+echo -n "1. runInitTest default parameters:"
+cat > $tempfile << .
+FATAL [example] MSG_WRITERR, error writing to test1: 42
+ERROR [example] MSG_RDLOCMES, reading local message file dummy/file
+WARN [example] MSG_BADSTREAM, bad log console output stream: example
+WARN [example.alpha] MSG_READERR, error reading from message file a.txt: dummy reason
+INFO [example.alpha] MSG_OPENIN, unable to open message file example.msg for input: dummy reason
+FATAL [example.beta] MSG_BADSEVERITY, unrecognized log severity: beta_fatal
+ERROR [example.beta] MSG_BADDESTINATION, unrecognized log destination: beta_error
+WARN [example.beta] MSG_BADSTREAM, bad log console output stream: beta_warn
+INFO [example.beta] MSG_READERR, error reading from message file beta: info
+.
+./logger_example -c stdout | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "2. Severity filter:"
+cat > $tempfile << .
+FATAL [example] MSG_WRITERR, error writing to test1: 42
+ERROR [example] MSG_RDLOCMES, reading local message file dummy/file
+FATAL [example.beta] MSG_BADSEVERITY, unrecognized log severity: beta_fatal
+ERROR [example.beta] MSG_BADDESTINATION, unrecognized log destination: beta_error
+.
+./logger_example -c stdout -s error | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "3. Debug level:"
+cat > $tempfile << .
+FATAL [example] MSG_WRITERR, error writing to test1: 42
+ERROR [example] MSG_RDLOCMES, reading local message file dummy/file
+WARN [example] MSG_BADSTREAM, bad log console output stream: example
+WARN [example.alpha] MSG_READERR, error reading from message file a.txt: dummy reason
+INFO [example.alpha] MSG_OPENIN, unable to open message file example.msg for input: dummy reason
+DEBUG [example] MSG_RDLOCMES, reading local message file example/0
+DEBUG [example] MSG_RDLOCMES, reading local message file example/24
+DEBUG [example] MSG_RDLOCMES, reading local message file example/25
+FATAL [example.beta] MSG_BADSEVERITY, unrecognized log severity: beta_fatal
+ERROR [example.beta] MSG_BADDESTINATION, unrecognized log destination: beta_error
+WARN [example.beta] MSG_BADSTREAM, bad log console output stream: beta_warn
+INFO [example.beta] MSG_READERR, error reading from message file beta: info
+DEBUG [example.beta] MSG_BADSEVERITY, unrecognized log severity: beta/25
+.
+./logger_example -c stdout -s debug -d 25 | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+if [ $failcount -eq 0 ]; then
+ echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+ echo "FAIL: $testname - 1 test failed"
+else
+ echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up
+rm -f $tempfile
+
+exit $failcount
diff --git a/src/lib/log/tests/tempdir.h.in b/src/lib/log/tests/tempdir.h.in
new file mode 100644
index 0000000..366fea3
--- /dev/null
+++ b/src/lib/log/tests/tempdir.h.in
@@ -0,0 +1,29 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __TEMPDIR_H
+#define __TEMPDIR_H
+
+/// \brief Define temporary directory
+///
+/// Defines the temporary directory in which temporary files used by the
+/// unit tests are created.
+
+#include <string>
+
+namespace {
+std::string TEMP_DIR("@builddir@");
+}
+
+#endif // __TEMPDIR_H
diff --git a/src/lib/log/tests/xdebuglevel_unittest.cc b/src/lib/log/tests/xdebuglevel_unittest.cc
deleted file mode 100644
index ca80e5a..0000000
--- a/src/lib/log/tests/xdebuglevel_unittest.cc
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <iostream>
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include <log4cxx/level.h>
-#include <log/xdebuglevel.h>
-#include <log/debug_levels.h>
-
-/// \brief XDebugLevel (Debug Extension to Level Class)
-///
-/// The class is an extension of the log4cxx Level class; this set of tests
-/// only test the extensions, they do not test the underlying Level class
-/// itself.
-
-using namespace log4cxx;
-
-class XDebugLevelTest : public ::testing::Test {
-protected:
- XDebugLevelTest()
- {
- }
-};
-
-// Check a basic assertion about the numeric values of the debug levels
-
-TEST_F(XDebugLevelTest, NumericValues) {
- EXPECT_EQ(XDebugLevel::XDEBUG_MIN_LEVEL_INT, Level::DEBUG_INT);
- EXPECT_EQ(XDebugLevel::XDEBUG_MAX_LEVEL_INT,
- Level::DEBUG_INT - MAX_DEBUG_LEVEL);
-
- // ... and check that assumptions used below - that the debug levels
- // range from 0 to 99 - are valid.
- EXPECT_EQ(0, MIN_DEBUG_LEVEL);
- EXPECT_EQ(99, MAX_DEBUG_LEVEL);
-}
-
-
-// Checks that the main function for generating logging level objects from
-// debug levels is working.
-
-TEST_F(XDebugLevelTest, GetExtendedDebug) {
-
- // Get a debug level of 0. This should be the same as the main DEBUG
- // level.
- LevelPtr debug0 = XDebugLevel::getExtendedDebug(0);
- EXPECT_EQ(std::string("DEBUG"), debug0->toString());
- EXPECT_EQ(Level::DEBUG_INT, debug0->toInt());
- EXPECT_TRUE(*Level::getDebug() == *debug0);
-
- // Get an arbitrary debug level in the allowed range.
- LevelPtr debug32 = XDebugLevel::getExtendedDebug(32);
- EXPECT_EQ(std::string("DEBUG32"), debug32->toString());
- EXPECT_TRUE((XDebugLevel::XDEBUG_MIN_LEVEL_INT - 32) == debug32->toInt());
-
- // Check that a value outside the range gives the nearest level.
- LevelPtr debug_more = XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL + 1);
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) == *debug_more);
-
- LevelPtr debug_less = XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL - 1);
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) == *debug_less);
-}
-
-
-// Creation of a level from an int - should return the default debug level
-// if outside the range.
-
-TEST_F(XDebugLevelTest, FromIntOneArg) {
-
- // Check that a valid debug level is as expected
- LevelPtr debug42 = XDebugLevel::toLevel(
- XDebugLevel::XDEBUG_MIN_LEVEL_INT - 42);
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(42) == *debug42);
-
- // ... and that an invalid one returns an object of type debug.
- LevelPtr debug_invalid = XDebugLevel::toLevel(Level::getInfo()->toInt());
- EXPECT_TRUE(*Level::getDebug() == *debug_invalid);
-}
-
-
-// Creation of a level from an int - should return the default level
-// if outside the range.
-
-TEST_F(XDebugLevelTest, FromIntTwoArg) {
-
- // Check that a valid debug level is as expected
- LevelPtr debug42 = XDebugLevel::toLevel(
- (XDebugLevel::XDEBUG_MIN_LEVEL_INT - 42), Level::getFatal());
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(42) == *debug42);
-
- // ... and that an invalid one returns an object of type debug.
- LevelPtr debug_invalid = XDebugLevel::toLevel(
- Level::getInfo()->toInt(), Level::getFatal());
- EXPECT_TRUE(*Level::getFatal() == *debug_invalid);
-}
-
-
-// Creation of a level from a string - should return the default debug level
-// if outside the range.
-
-TEST_F(XDebugLevelTest, FromStringOneArg) {
-
- // Check that a valid debug levels are as expected
- LevelPtr debug85 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG85"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(85) == *debug85);
-
- LevelPtr debug92 = XDebugLevel::toLevelLS(LOG4CXX_STR("debug92"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(92) == *debug92);
-
- LevelPtr debug27 = XDebugLevel::toLevelLS(LOG4CXX_STR("Debug27"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(27) == *debug27);
-
- LevelPtr debug0 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug0);
-
- // ... and that an invalid one returns an object of type debug (which is
- // the equivalent of a debug level 0 object).
- LevelPtr debug_invalid1 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBU"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid1);
-
- LevelPtr debug_invalid2 = XDebugLevel::toLevelLS(LOG4CXX_STR("EBU"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid2);
-
- LevelPtr debug_invalid3 = XDebugLevel::toLevelLS(LOG4CXX_STR(""));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid3);
-
- LevelPtr debug_invalid4 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUGTEN"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid4);
-
- LevelPtr debug_invalid5 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG105"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) ==
- *debug_invalid5);
-
- LevelPtr debug_invalid6 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG-7"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) ==
- *debug_invalid6);
-}
-
-
-// Creation of a level from a string - should return the default level
-// if outside the range.
-
-TEST_F(XDebugLevelTest, FromStringTwoArg) {
-
- // Check that a valid debug levels are as expected
- LevelPtr debug85 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG85"),
- Level::getFatal());
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(85) == *debug85);
-
- LevelPtr debug92 = XDebugLevel::toLevelLS(LOG4CXX_STR("debug92"),
- Level::getFatal());
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(92) == *debug92);
-
- LevelPtr debug27 = XDebugLevel::toLevelLS(LOG4CXX_STR("Debug27"),
- Level::getFatal());
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(27) == *debug27);
-
- LevelPtr debug0 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG"),
- Level::getFatal());
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug0);
-
- // ... and that an invalid one returns an object of type debug (which is
- // the equivalent of a debug level 0 object).
- LevelPtr debug_invalid1 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBU"),
- Level::getFatal());
- EXPECT_TRUE(*Level::getFatal() == *debug_invalid1);
-
- LevelPtr debug_invalid2 = XDebugLevel::toLevelLS(LOG4CXX_STR("EBU"),
- Level::getFatal());
- EXPECT_TRUE(*Level::getFatal() == *debug_invalid2);
-
- LevelPtr debug_invalid3 = XDebugLevel::toLevelLS(LOG4CXX_STR(""),
- Level::getFatal());
- EXPECT_TRUE(*Level::getFatal() == *debug_invalid3);
-
- LevelPtr debug_invalid4 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUGTEN"),
- Level::getFatal());
- EXPECT_TRUE(*Level::getFatal() == *debug_invalid4);
-
- LevelPtr debug_invalid5 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG105"),
- Level::getFatal());
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) ==
- *debug_invalid5);
-
- LevelPtr debug_invalid6 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG-7"),
- Level::getFatal());
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) ==
- *debug_invalid6);
-}
diff --git a/src/lib/log/xdebuglevel.cc b/src/lib/log/xdebuglevel.cc
deleted file mode 100644
index c17a515..0000000
--- a/src/lib/log/xdebuglevel.cc
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <cassert>
-#include <algorithm>
-#include <syslog.h>
-#include <string.h>
-#include <boost/lexical_cast.hpp>
-
-#include <xdebuglevel.h>
-#include <debug_levels.h>
-#include <log4cxx/helpers/stringhelper.h>
-
-using namespace log4cxx;
-using namespace log4cxx::helpers;
-
-// Storage for the logging level objects corresponding to each debug level
-
-bool XDebugLevel::dbglevels_unset_ = true;
-LevelPtr XDebugLevel::dbglevels_[NUM_DEBUG_LEVEL];
-
-// Register the class
-
-IMPLEMENT_LOG4CXX_LEVEL(XDebugLevel)
-
-
-// Create Extended Debug Level Objects
-
-LevelPtr
-XDebugLevel::getExtendedDebug(int level) {
-
- // Initialize the logging levels corresponding to the possible range of
- // debug if we have not already done so
- if (dbglevels_unset_) {
-
- // Asserting that the minimum debug level is zero - so corresponds
- // to DEBUG_INT - means that the lowest level is set to main DEBUG
- // level. This means that the existing logging level object can be
- // used.
- assert(MIN_DEBUG_LEVEL == 0);
- dbglevels_[0] = Level::getDebug();
-
- // Create the logging level objects for the rest of the debug levels.
- // They are given names of the form DEBUG<debug level> (e.g. DEBUG42).
- // They will all correspond to a syslog level of DEBUG.
- for (int i = 1; i < NUM_DEBUG_LEVEL; ++i) {
- std::string name = std::string("DEBUG") +
- boost::lexical_cast<std::string>(i);
- dbglevels_[i] = new XDebugLevel(
- (XDebugLevel::XDEBUG_MIN_LEVEL_INT - i),
- LOG4CXX_STR(name.c_str()), LOG_DEBUG);
- }
- dbglevels_unset_ = false;
- }
-
- // Now get the logging level object asked for. Coerce the debug level to
- // lie in the acceptable range.
- int actual = std::max(MIN_DEBUG_LEVEL, std::min(MAX_DEBUG_LEVEL, level));
-
- // ... and return a pointer to the appropriate logging level object
- return (dbglevels_[actual - MIN_DEBUG_LEVEL]);
-}
-
-// Convert an integer (an absolute logging level number, not a debug level) to a
-// logging level object. If it lies outside the valid range, an object
-// corresponding to the minimum debug value is returned.
-
-LevelPtr
-XDebugLevel::toLevel(int val) {
- return (toLevel(val, getExtendedDebug(MIN_DEBUG_LEVEL)));
-}
-
-LevelPtr
-XDebugLevel::toLevel(int val, const LevelPtr& defaultLevel) {
-
- // Note the reversal of the notion of MIN and MAX - see the header file for
- // details.
- if ((val >= XDEBUG_MAX_LEVEL_INT) && (val <= XDEBUG_MIN_LEVEL_INT)) {
- return (getExtendedDebug(XDEBUG_MIN_LEVEL_INT - val));
- }
- else {
- return (defaultLevel);
- }
-}
-
-// Convert string passed to a logging level or return default level.
-
-LevelPtr
-XDebugLevel::toLevelLS(const LogString& sArg) {
- return (toLevelLS(sArg, getExtendedDebug(0)));
-}
-
-LevelPtr
-XDebugLevel::toLevelLS(const LogString& sArg, const LevelPtr& defaultLevel) {
- std::string name = sArg; // Get to known type
- size_t length = name.size(); // Length of the string
-
- if (length < 5) {
-
- // String can't possibly start DEBUG so we don't know what it is.
- return (defaultLevel);
- }
- else {
- if (strncasecmp(name.c_str(), "DEBUG", 5) == 0) {
-
- // String starts "DEBUG" (or "debug" or any case mixture). The
- // rest of the string -if any - should be a number.
- if (length == 5) {
-
- // It is plain "DEBUG". Take this as level 0.
- return (getExtendedDebug(0));
- }
- else {
-
- // Try converting the remainder to an integer. The "5" is
- // the length of the string "DEBUG". Note that if the number
- // is outside the rangeof debug levels, it is coerced to the
- // nearest limit. Thus a level of DEBUG509 will end up as
- // if DEBUG99 has been specified.
- try {
- int level = boost::lexical_cast<int>(name.substr(5));
- return (getExtendedDebug(level));
- }
- catch ((boost::bad_lexical_cast&) ){
- return (defaultLevel);
- }
- }
- }
- else {
-
- // Unknown string - return default.
- return (defaultLevel);
- }
- }
-}
diff --git a/src/lib/log/xdebuglevel.h b/src/lib/log/xdebuglevel.h
deleted file mode 100644
index e580b77..0000000
--- a/src/lib/log/xdebuglevel.h
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __XDEBUGLEVEL_H
-#define __XDEBUGLEVEL_H
-
-#include <syslog.h>
-#include <log4cxx/level.h>
-
-#include <debug_levels.h>
-
-namespace log4cxx {
-
-/// \brief Debug Extension to Level Class
-///
-/// Based on the example given in the log4cxx distribution, this extends the
-/// log4cxx Level class to allow 100 debug levels.
-///
-/// First some terminology, as the use of the term "level" gets confusing. The
-/// code and comments here use the term "level" in two contexts:
-///
-/// Logging level: The category of messages to log. By default log4cxx defines
-/// the following logging levels: OFF, FATAL, ERROR, WARNING, INFO, DEBUG,
-/// TRACE, ALL. Within the context of BIND-10, OFF, TRACE and ALL are not used
-/// and the idea of DEBUG has been extended, as will be seen below.
-///
-/// Debug level: This is a number that ranges from 0 to 99 and is used by the
-/// application to control the detail of debug output. A value of 0 gives the
-/// highest-level debug output; a value of 99 gives the most verbose and most
-/// detailed. Debug messages (or whatever debug level) are only ever output
-/// when the logging level is set to DEBUG.
-///
-///
-/// With log4cxx, the various logging levels have a numeric value associated
-/// with them, such that FATAL > ERROR > WARNING etc. This suggests that the
-/// idea of debug levels can be incorporated into the existing logging level
-/// scheme by assigning them appropriate numeric values, i.e.
-///
-/// WARNING > INFO > DEBUG(0) > DEBUG(2) > ... > DEBUG(99)
-///
-/// Setting a numeric level of DEBUG enables the basic messages; setting lower
-/// numeric levels will enable progressively more messages. The lowest debug
-/// level (0) is chosen such that setting the general DEBUG logging level will
-/// automatically select that debug level.
-///
-/// This sub-class is needed because the log4cxx::Level class does not allow
-/// the setting of the numeric value of the current level to something other
-/// than the values enumerated in the class. It creates a set of log4cxx
-/// logging levels to correspond to the various debug levels. These levels have
-/// names in the range DEBUG1 to DEBUG99 (the existing Level DEBUG is used for
-/// a debug level of 0), although they are not used in BIND-10: instead the
-/// BIND-10 Logger class treats the logging levels and debug levels separately
-/// and combines them to choose the underlying log4cxx logging level.
-
-
-/// \brief Debug-Extended Level
-
-class XDebugLevel : public Level {
- DECLARE_LOG4CXX_LEVEL(XDebugLevel)
-
- /// Array of pointers to logging level objects, one for each debug level.
- /// The pointer corresponding to a debug level of 0 points to the DEBUG
- /// logging level object.
- static LevelPtr dbglevels_[NUM_DEBUG_LEVEL];
- static bool dbglevels_unset_;
-
-public:
-
- // Minimum and maximum debug levels. Note that XDEBUG_MIN_LEVEL_INT is the
- // number corresponding to the minimum debug level - and is actually larger
- // that XDEBUG_MAX_LEVEL_INT, the number corresponding to the maximum debug
- // level.
- enum {
- XDEBUG_MIN_LEVEL_INT = Level::DEBUG_INT - MIN_DEBUG_LEVEL,
- XDEBUG_MAX_LEVEL_INT = Level::DEBUG_INT - MAX_DEBUG_LEVEL
- };
-
- /// \brief Constructor
- ///
- /// \param level Numeric value of the logging level.
- /// \param name Name given to this logging level.
- /// \param syslogEquivalent The category to be used by syslog when it logs
- /// an event associated with the specified logging level.
- XDebugLevel(int level, const LogString& name, int syslogEquivalent) :
- Level(level, name, syslogEquivalent)
- {}
-
- /// \brief Create Logging Level Object
- ///
- /// Creates a logging level object corresponding to one of the debug levels.
- ///
- /// \param dbglevel The debug level, which ranges from MIN_DEBUG_LEVEL to
- /// MAX_DEBUG_LEVEL. It is coerced to that range if it lies outside it.
- ///
- /// \return Pointer to the desired logging level object.
- static LevelPtr getExtendedDebug(int dbglevel);
-
- /// \brief Convert Integer to a Logging Level
- ///
- /// Returns a logging level object corresponding to the given value (which
- /// is an absolute value of a logging level - it is not a debug level).
- /// If the number is invalid, an object of logging level DEBUG (the
- /// minimum debug logging level) is returned.
- ///
- /// \param val Number to convert to a logging level. This is an absolute
- /// logging level number, not a debug level.
- ///
- /// \return Pointer to the desired logging level object.
- static LevelPtr toLevel(int val);
-
- /// \brief Convert Integer to a Level
- ///
- /// Returns a logging level object corresponding to the given value (which
- /// is an absolute value of a logging level - it is not a debug level).
- /// If the number is invalid, the given default is returned.
- ///
- /// \param val Number to convert to a logging level. This is an absolute
- /// logging level number, not a debug level.
- /// \param defaultLevel Logging level to return if value is not recognised.
- ///
- /// \return Pointer to the desired logging level object.
- static LevelPtr toLevel(int val, const LevelPtr& defaultLevel);
-
- /// \brief Convert String to Logging Level
- ///
- /// Returns a logging level object corresponding to the given name. If the
- /// name is invalid, an object of logging level DEBUG (the minimum debug
- /// logging level) is returned.
- ///
- /// \param sArg Name of the logging level.
- ///
- /// \return Pointer to the desired logging level object.
- static LevelPtr toLevelLS(const LogString& sArg);
-
- /// \brief Convert String to Logging Level
- ///
- /// Returns a logging level object corresponding to the given name. If the
- /// name is invalid, the given default is returned.
- ///
- /// \param sArg name of the level.
- /// \param defaultLevel Logging level to return if name doesn't exist.
- ///
- /// \return Pointer to the desired logging level object.
- static LevelPtr toLevelLS(const LogString& sArg,
- const LevelPtr& defaultLevel);
-};
-
-} // namespace log4cxx
-
-
-#endif // __XDEBUGLEVEL_H
diff --git a/src/lib/nsas/tests/Makefile.am b/src/lib/nsas/tests/Makefile.am
index e9235ba..420e897 100644
--- a/src/lib/nsas/tests/Makefile.am
+++ b/src/lib/nsas/tests/Makefile.am
@@ -43,8 +43,8 @@ run_unittests_SOURCES += zone_entry_unittest.cc
run_unittests_SOURCES += fetchable_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
# NOTE: we may have to clean up this hack later (see the note in configure.ac)
if NEED_LIBBOOST_THREAD
@@ -56,6 +56,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/nsas/tests/run_unittests.cc b/src/lib/nsas/tests/run_unittests.cc
index bc672d0..e469e03 100644
--- a/src/lib/nsas/tests/run_unittests.cc
+++ b/src/lib/nsas/tests/run_unittests.cc
@@ -12,24 +12,13 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <config.h>
-#include <stdlib.h>
-
-#include <string>
-#include <boost/lexical_cast.hpp>
-
#include <gtest/gtest.h>
-
-#include <dns/tests/unittest_util.h>
#include <log/logger_support.h>
-
-using namespace std;
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
-
isc::log::initLogger();
-
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/python/bind10_config.py.in b/src/lib/python/bind10_config.py.in
index 4a38903..69b17ed 100644
--- a/src/lib/python/bind10_config.py.in
+++ b/src/lib/python/bind10_config.py.in
@@ -42,7 +42,8 @@ def reload():
DATA_PATH = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"]
else:
DATA_PATH = os.environ["B10_FROM_SOURCE"]
- PLUGIN_PATHS = [DATA_PATH + '/src/bin/cfgmgr/plugins']
+ PLUGIN_PATHS = [os.environ["B10_FROM_SOURCE"] +
+ '/src/bin/cfgmgr/plugins']
else:
DATA_PATH = "@localstatedir@/@PACKAGE@".replace("${prefix}", PREFIX)
PLUGIN_PATHS = ["@prefix@/share/@PACKAGE@/config_plugins"]
diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py
index 226c6ba..84809f1 100644
--- a/src/lib/python/isc/config/ccsession.py
+++ b/src/lib/python/isc/config/ccsession.py
@@ -329,10 +329,18 @@ class ModuleCCSession(ConfigData):
if answer:
rcode, value = parse_answer(answer)
if rcode == 0:
- if value != None and self.get_module_spec().validate_config(False, value):
- self.set_local_config(value);
- if self._config_handler:
- self._config_handler(value)
+ errors = []
+ if value != None:
+ if self.get_module_spec().validate_config(False,
+ value,
+ errors):
+ self.set_local_config(value);
+ if self._config_handler:
+ self._config_handler(value)
+ else:
+ raise ModuleCCSessionError(
+ "Wrong data in configuration: " +
+ " ".join(errors))
else:
# log error
print("[" + self._module_name + "] Error requesting configuration: " + value)
diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py
index 88a93e1..f46daf8 100644
--- a/src/lib/python/isc/config/cfgmgr.py
+++ b/src/lib/python/isc/config/cfgmgr.py
@@ -214,7 +214,7 @@ class ConfigManager:
is returned"""
if module_name:
if module_name in self.module_specs:
- return self.module_specs[module_name]
+ return self.module_specs[module_name].get_full_spec()
else:
# TODO: log error?
return {}
@@ -272,7 +272,12 @@ class ConfigManager:
if type(cmd) == dict:
if 'module_name' in cmd and cmd['module_name'] != '':
module_name = cmd['module_name']
- answer = ccsession.create_answer(0, self.get_module_spec(module_name))
+ spec = self.get_module_spec(cmd['module_name'])
+ if type(spec) != type({}):
+ # this is a ModuleSpec object. Extract the
+ # internal spec.
+ spec = spec.get_full_spec()
+ answer = ccsession.create_answer(0, spec)
else:
answer = ccsession.create_answer(1, "Bad module_name in get_module_spec command")
else:
diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py
index cc3f484..1efe4a9 100644
--- a/src/lib/python/isc/config/config_data.py
+++ b/src/lib/python/isc/config/config_data.py
@@ -108,6 +108,52 @@ def convert_type(spec_part, value):
except TypeError as err:
raise isc.cc.data.DataTypeError(str(err))
+def _get_map_or_list(spec_part):
+ """Returns the list or map specification if this is a list or a
+ map specification part. If not, returns the given spec_part
+ itself"""
+ if "map_item_spec" in spec_part:
+ return spec_part["map_item_spec"]
+ elif "list_item_spec" in spec_part:
+ return spec_part["list_item_spec"]
+ else:
+ return spec_part
+
+def _find_spec_part_single(cur_spec, id_part):
+ """Find the spec part for the given (partial) name. This partial
+ name does not contain separators ('/'), and the specification
+ part should be a direct child of the given specification part.
+ id_part may contain list selectors, which will be ignored.
+ Returns the child part.
+ Raises DataNotFoundError if it was not found."""
+ # strip list selector part
+ # don't need it for the spec part, so just drop it
+ id, list_indices = isc.cc.data.split_identifier_list_indices(id_part)
+
+ # The specification we want a sub-part for should be either a
+ # list or a map, which is internally represented by a dict with
+ # an element 'map_item_spec', a dict with an element 'list_item_spec',
+ # or a list (when it is the 'main' config_data element of a module).
+ if type(cur_spec) == dict and 'map_item_spec' in cur_spec.keys():
+ for cur_spec_item in cur_spec['map_item_spec']:
+ if cur_spec_item['item_name'] == id:
+ return cur_spec_item
+ # not found
+ raise isc.cc.data.DataNotFoundError(id + " not found")
+ elif type(cur_spec) == dict and 'list_item_spec' in cur_spec.keys():
+ if cur_spec['item_name'] == id:
+ return cur_spec['list_item_spec']
+ # not found
+ raise isc.cc.data.DataNotFoundError(id + " not found")
+ elif type(cur_spec) == list:
+ for cur_spec_item in cur_spec:
+ if cur_spec_item['item_name'] == id:
+ return cur_spec_item
+ # not found
+ raise isc.cc.data.DataNotFoundError(id + " not found")
+ else:
+ raise isc.cc.data.DataNotFoundError("Not a correct config specification")
+
def find_spec_part(element, identifier):
"""find the data definition for the given identifier
returns either a map with 'item_name' etc, or a list of those"""
@@ -117,38 +163,15 @@ def find_spec_part(element, identifier):
id_parts[:] = (value for value in id_parts if value != "")
cur_el = element
- for id_part in id_parts:
- # strip list selector part
- # don't need it for the spec part, so just drop it
- id, list_indices = isc.cc.data.split_identifier_list_indices(id_part)
- # is this part still needed? (see below)
- if type(cur_el) == dict and 'map_item_spec' in cur_el.keys():
- found = False
- for cur_el_item in cur_el['map_item_spec']:
- if cur_el_item['item_name'] == id:
- cur_el = cur_el_item
- found = True
- if not found:
- raise isc.cc.data.DataNotFoundError(id + " not found")
- elif type(cur_el) == dict and 'list_item_spec' in cur_el.keys():
- cur_el = cur_el['list_item_spec']
- elif type(cur_el) == list:
- found = False
- for cur_el_item in cur_el:
- if cur_el_item['item_name'] == id:
- cur_el = cur_el_item
- # if we need to go further, we may need to 'skip' a step here
- # but not if we're done
- if id_parts[-1] != id_part and type(cur_el) == dict:
- if "map_item_spec" in cur_el:
- cur_el = cur_el["map_item_spec"]
- elif "list_item_spec" in cur_el:
- cur_el = cur_el["list_item_spec"]
- found = True
- if not found:
- raise isc.cc.data.DataNotFoundError(id + " not found")
- else:
- raise isc.cc.data.DataNotFoundError("Not a correct config specification")
+ # up to the last element, if the result is a map or a list,
+ # we want its subspecification (i.e. list_item_spec or
+ # map_item_spec). For the last element in the identifier we
+ # always want the 'full' spec of the item
+ for id_part in id_parts[:-1]:
+ cur_el = _find_spec_part_single(cur_el, id_part)
+ cur_el = _get_map_or_list(cur_el)
+
+ cur_el = _find_spec_part_single(cur_el, id_parts[-1])
return cur_el
def spec_name_list(spec, prefix="", recurse=False):
diff --git a/src/lib/python/isc/config/module_spec.py b/src/lib/python/isc/config/module_spec.py
index 6c90677..6171149 100644
--- a/src/lib/python/isc/config/module_spec.py
+++ b/src/lib/python/isc/config/module_spec.py
@@ -87,7 +87,7 @@ class ModuleSpec:
validate only a part of a configuration tree (like a list of
non-default values)"""
data_def = self.get_config_spec()
- if data_def:
+ if data_def is not None:
return _validate_spec_list(data_def, full, data, errors)
else:
# no spec, always bad
@@ -345,7 +345,7 @@ def _validate_spec_list(module_spec, full, data, errors):
for spec_item in module_spec:
if spec_item["item_name"] == item_name:
found = True
- if not found:
+ if not found and item_name != "version":
if errors != None:
errors.append("unknown item " + item_name)
validated = False
diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py
index 4edc559..a0cafd6 100644
--- a/src/lib/python/isc/config/tests/ccsession_test.py
+++ b/src/lib/python/isc/config/tests/ccsession_test.py
@@ -220,6 +220,31 @@ class TestModuleCCSession(unittest.TestCase):
self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
fake_session.get_message('ConfigManager', None))
+ def test_start5(self):
+ fake_session = FakeModuleCCSession()
+ mccs = self.create_session("spec2.spec", None, None, fake_session)
+ mccs.set_config_handler(self.my_config_handler_ok)
+ self.assertEqual(len(fake_session.message_queue), 0)
+ fake_session.group_sendmsg(None, 'Spec2')
+ fake_session.group_sendmsg(None, 'Spec2')
+ self.assertRaises(ModuleCCSessionError, mccs.start)
+ self.assertEqual(len(fake_session.message_queue), 2)
+ self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
+ fake_session.get_message('ConfigManager', None))
+ self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
+ fake_session.get_message('ConfigManager', None))
+
+ self.assertEqual(len(fake_session.message_queue), 0)
+ fake_session.group_sendmsg({'result': [ 0 ]}, "Spec2")
+ fake_session.group_sendmsg({'result': [ 0, {"Wrong": True} ]}, "Spec2")
+ self.assertRaises(ModuleCCSessionError, mccs.start)
+ self.assertEqual(len(fake_session.message_queue), 2)
+
+ self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
+ fake_session.get_message('ConfigManager', None))
+ self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
+ fake_session.get_message('ConfigManager', None))
+
def test_get_socket(self):
fake_session = FakeModuleCCSession()
mccs = self.create_session("spec1.spec", None, None, fake_session)
diff --git a/src/lib/python/isc/config/tests/cfgmgr_test.py b/src/lib/python/isc/config/tests/cfgmgr_test.py
index b06db31..0a9e2d3 100644
--- a/src/lib/python/isc/config/tests/cfgmgr_test.py
+++ b/src/lib/python/isc/config/tests/cfgmgr_test.py
@@ -176,7 +176,9 @@ class TestConfigManager(unittest.TestCase):
self.cm.set_module_spec(module_spec)
self.assert_(module_spec.get_module_name() in self.cm.module_specs)
module_spec2 = self.cm.get_module_spec(module_spec.get_module_name())
- self.assertEqual(module_spec, module_spec2)
+ self.assertEqual(module_spec.get_full_spec(), module_spec2)
+
+ self.assertEqual({}, self.cm.get_module_spec("nosuchmodule"))
def test_get_config_spec(self):
config_spec = self.cm.get_config_spec()
@@ -323,6 +325,9 @@ class TestConfigManager(unittest.TestCase):
self._handle_msg_helper({ "command": [ "module_spec", { 'foo': 1 } ] },
{'result': [1, 'Error in data definition: no module_name in module_spec']})
self._handle_msg_helper({ "command": [ "get_module_spec" ] }, { 'result': [ 0, { self.spec.get_module_name(): self.spec.get_full_spec() } ]})
+ self._handle_msg_helper({ "command": [ "get_module_spec",
+ { "module_name" : "Spec2" } ] },
+ { 'result': [ 0, self.spec.get_full_spec() ] })
self._handle_msg_helper({ "command": [ "get_commands_spec" ] }, { 'result': [ 0, { self.spec.get_module_name(): self.spec.get_commands_spec() } ]})
# re-add this once we have new way to propagate spec changes (1 instead of the current 2 messages)
#self.assertEqual(len(self.fake_session.message_queue), 2)
diff --git a/src/lib/python/isc/config/tests/config_data_test.py b/src/lib/python/isc/config/tests/config_data_test.py
index 48099bb..fc1bffa 100644
--- a/src/lib/python/isc/config/tests/config_data_test.py
+++ b/src/lib/python/isc/config/tests/config_data_test.py
@@ -329,6 +329,35 @@ class TestMultiConfigData(unittest.TestCase):
spec_part = self.mcd.find_spec_part("Spec2/item1")
self.assertEqual({'item_name': 'item1', 'item_type': 'integer', 'item_optional': False, 'item_default': 1, }, spec_part)
+ def test_find_spec_part_nested(self):
+ module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec30.spec")
+ self.mcd.set_specification(module_spec)
+ spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/second_list_items[1]/final_element")
+ self.assertEqual({'item_name': 'final_element', 'item_type': 'string', 'item_default': 'hello', 'item_optional': False}, spec_part)
+ spec_part = self.mcd.find_spec_part("/BAD_NAME/first_list_items[0]/second_list_items[1]/final_element")
+ self.assertEqual(None, spec_part)
+
+ def test_find_spec_part_nested2(self):
+ module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec31.spec")
+ self.mcd.set_specification(module_spec)
+ spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/second_list_items[1]/map_element/list1[1]/list2[2]")
+ self.assertEqual({"item_name": "number", "item_type": "integer", "item_optional": False, "item_default": 1}, spec_part)
+
+ spec_part = self.mcd.find_spec_part("/DOESNOTEXIST")
+ self.assertEqual(None, spec_part)
+ spec_part = self.mcd.find_spec_part("/lists/DOESNOTEXIST")
+ self.assertEqual(None, spec_part)
+ spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/DOESNOTEXIST")
+ self.assertEqual(None, spec_part)
+ spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/second_list_items[1]/DOESNOTEXIST")
+ self.assertEqual(None, spec_part)
+ spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/second_list_items[1]/map_element/DOESNOTEXIST")
+ self.assertEqual(None, spec_part)
+ spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/second_list_items[1]/map_element/list1[1]/DOESNOTEXIST")
+ self.assertEqual(None, spec_part)
+ spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/second_list_items[1]/map_element/list1[1]/list2[1]/DOESNOTEXIST")
+ self.assertEqual(None, spec_part)
+
def test_get_current_config(self):
cf = { 'module1': { 'item1': 2, 'item2': True } }
self.mcd._set_current_config(cf);
diff --git a/src/lib/python/isc/datasrc/sqlite3_ds.py b/src/lib/python/isc/datasrc/sqlite3_ds.py
index 77b0828..a77645a 100644
--- a/src/lib/python/isc/datasrc/sqlite3_ds.py
+++ b/src/lib/python/isc/datasrc/sqlite3_ds.py
@@ -235,13 +235,13 @@ def load(dbfile, zone, reader):
zone += '.'
conn, cur = open(dbfile)
- old_zone_id = get_zoneid(zone, cur)
+ try:
+ old_zone_id = get_zoneid(zone, cur)
- temp = str(random.randrange(100000))
- cur.execute("INSERT INTO zones (name, rdclass) VALUES (?, 'IN')", [temp])
- new_zone_id = cur.lastrowid
+ temp = str(random.randrange(100000))
+ cur.execute("INSERT INTO zones (name, rdclass) VALUES (?, 'IN')", [temp])
+ new_zone_id = cur.lastrowid
- try:
for name, ttl, rdclass, rdtype, rdata in reader():
sigtype = ''
if rdtype.lower() == 'rrsig':
@@ -266,20 +266,20 @@ def load(dbfile, zone, reader):
VALUES (?, ?, ?, ?, ?, ?)""",
[new_zone_id, name, reverse_name(name), ttl,
rdtype, rdata])
+
+ if old_zone_id:
+ cur.execute("DELETE FROM zones WHERE id=?", [old_zone_id])
+ cur.execute("UPDATE zones SET name=? WHERE id=?", [zone, new_zone_id])
+ conn.commit()
+ cur.execute("DELETE FROM records WHERE zone_id=?", [old_zone_id])
+ cur.execute("DELETE FROM nsec3 WHERE zone_id=?", [old_zone_id])
+ conn.commit()
+ else:
+ cur.execute("UPDATE zones SET name=? WHERE id=?", [zone, new_zone_id])
+ conn.commit()
except Exception as e:
fail = "Error while loading " + zone + ": " + e.args[0]
raise Sqlite3DSError(fail)
-
- if old_zone_id:
- cur.execute("DELETE FROM zones WHERE id=?", [old_zone_id])
- cur.execute("UPDATE zones SET name=? WHERE id=?", [zone, new_zone_id])
- conn.commit()
- cur.execute("DELETE FROM records WHERE zone_id=?", [old_zone_id])
- cur.execute("DELETE FROM nsec3 WHERE zone_id=?", [old_zone_id])
- conn.commit()
- else:
- cur.execute("UPDATE zones SET name=? WHERE id=?", [zone, new_zone_id])
- conn.commit()
-
- cur.close()
- conn.close()
+ finally:
+ cur.close()
+ conn.close()
diff --git a/src/lib/python/isc/datasrc/tests/Makefile.am b/src/lib/python/isc/datasrc/tests/Makefile.am
index 4f87cc9..a6c5c58 100644
--- a/src/lib/python/isc/datasrc/tests/Makefile.am
+++ b/src/lib/python/isc/datasrc/tests/Makefile.am
@@ -4,6 +4,7 @@ EXTRA_DIST = $(PYTESTS)
EXTRA_DIST += testdata/brokendb.sqlite3
EXTRA_DIST += testdata/example.com.sqlite3
+CLEANFILES = $(abs_builddir)/example.com.out.sqlite3
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
@@ -16,5 +17,6 @@ endif
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/log \
TESTDATA_PATH=$(abs_srcdir)/testdata \
+ TESTDATA_WRITE_PATH=$(abs_builddir) \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
diff --git a/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py b/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py
index 013c7d7..707994f 100644
--- a/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py
+++ b/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py
@@ -17,8 +17,31 @@ from isc.datasrc import sqlite3_ds
import os
import socket
import unittest
+import sqlite3
TESTDATA_PATH = os.environ['TESTDATA_PATH'] + os.sep
+TESTDATA_WRITE_PATH = os.environ['TESTDATA_WRITE_PATH'] + os.sep
+
+READ_ZONE_DB_FILE = TESTDATA_PATH + "example.com.sqlite3"
+WRITE_ZONE_DB_FILE = TESTDATA_WRITE_PATH + "example.com.out.sqlite3"
+BROKEN_DB_FILE = TESTDATA_PATH + "brokendb.sqlite3"
+
+def example_reader():
+ my_zone = [
+ ("example.com.", "3600", "IN", "SOA", "ns.example.com. admin.example.com. 1234 3600 1800 2419200 7200"),
+ ("example.com.", "3600", "IN", "NS", "ns.example.com."),
+ ("ns.example.com.", "3600", "IN", "A", "192.0.2.1")
+ ]
+ for rr in my_zone:
+ yield rr
+
+def example_reader_nested():
+ # this iterator is used in the 'locked' test; it will cause
+ # the load() method to try and write to the same database
+ sqlite3_ds.load(WRITE_ZONE_DB_FILE,
+ ".",
+ example_reader)
+ return example_reader()
class TestSqlite3_ds(unittest.TestCase):
def test_zone_exist(self):
@@ -33,11 +56,40 @@ class TestSqlite3_ds(unittest.TestCase):
# Open a broken database file
self.assertRaises(sqlite3_ds.Sqlite3DSError,
sqlite3_ds.zone_exist, "example.com",
- TESTDATA_PATH + "brokendb.sqlite3")
+ BROKEN_DB_FILE)
self.assertTrue(sqlite3_ds.zone_exist("example.com.",
- TESTDATA_PATH + "example.com.sqlite3"))
+ READ_ZONE_DB_FILE))
self.assertFalse(sqlite3_ds.zone_exist("example.org.",
- TESTDATA_PATH + "example.com.sqlite3"))
+ READ_ZONE_DB_FILE))
+
+ def test_load_db(self):
+ sqlite3_ds.load(WRITE_ZONE_DB_FILE, ".", example_reader)
+
+ def test_locked_db(self):
+ # load it first to make sure it exists
+ sqlite3_ds.load(WRITE_ZONE_DB_FILE, ".", example_reader)
+
+ # and manually create a writing session as well
+ con = sqlite3.connect(WRITE_ZONE_DB_FILE);
+ cur = con.cursor()
+ cur.execute("delete from records")
+
+ self.assertRaises(sqlite3_ds.Sqlite3DSError,
+ sqlite3_ds.load, WRITE_ZONE_DB_FILE, ".",
+ example_reader)
+
+ con.rollback()
+
+ # and make sure lock does not stay
+ sqlite3_ds.load(WRITE_ZONE_DB_FILE, ".", example_reader)
+
+ # force locked db by nested loads
+ self.assertRaises(sqlite3_ds.Sqlite3DSError,
+ sqlite3_ds.load, WRITE_ZONE_DB_FILE, ".",
+ example_reader_nested)
+
+ # and make sure lock does not stay
+ sqlite3_ds.load(WRITE_ZONE_DB_FILE, ".", example_reader)
if __name__ == '__main__':
unittest.main()
diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py
index 178a983..c3b9939 100644
--- a/src/lib/python/isc/notify/notify_out.py
+++ b/src/lib/python/isc/notify/notify_out.py
@@ -21,6 +21,7 @@ import threading
import time
import errno
from isc.datasrc import sqlite3_ds
+from isc.net import addr
import isc
try:
from pydnspp import *
@@ -66,7 +67,7 @@ class ZoneNotifyInfo:
self.zone_name = zone_name_
self.zone_class = class_
self.notify_msg_id = 0
- self.notify_timeout = 0
+ self.notify_timeout = None
self.notify_try_num = 0 #Notify times sending to one target.
def set_next_notify_target(self):
@@ -77,8 +78,7 @@ class ZoneNotifyInfo:
self._notify_current = None
def prepare_notify_out(self):
- '''Create the socket and set notify timeout time to now'''
- self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #TODO support IPv6?
+ '''Set notify timeout time to now'''
self.notify_timeout = time.time()
self.notify_try_num = 0
self._slave_index = 0
@@ -89,6 +89,12 @@ class ZoneNotifyInfo:
if self._sock:
self._sock.close()
self._sock = None
+ self.notify_timeout = None
+
+ def create_socket(self, dest_addr):
+ self._sock = socket.socket(addr.IPAddr(dest_addr).family,
+ socket.SOCK_DGRAM)
+ return self._sock
def get_socket(self):
return self._sock
@@ -270,8 +276,15 @@ class NotifyOut:
sock = self._notify_infos[info].get_socket()
if sock:
valid_socks.append(sock)
+
+ # If a non null timeout is specified notify has been scheduled
+ # (in which case socket is still None) or sent (with a valid
+ # socket). In either case we need add the zone to notifying_zones
+ # so that we can invoke the appropriate event for the zone after
+ # select.
+ tmp_timeout = self._notify_infos[info].notify_timeout
+ if tmp_timeout is not None:
notifying_zones[info] = self._notify_infos[info]
- tmp_timeout = self._notify_infos[info].notify_timeout
if min_timeout is not None:
if tmp_timeout < min_timeout:
min_timeout = tmp_timeout
@@ -380,12 +393,13 @@ class NotifyOut:
render.set_length_limit(512)
msg.to_wire(render)
zone_notify_info.notify_msg_id = qid
- sock = zone_notify_info.get_socket()
try:
+ sock = zone_notify_info.create_socket(addrinfo[0])
sock.sendto(render.get_data(), 0, addrinfo)
self._log_msg('info', 'sending notify to %s' % addr_to_str(addrinfo))
- except socket.error as err:
- self._log_msg('error', 'send notify to %s failed: %s' % (addr_to_str(addrinfo), str(err)))
+ except (socket.error, addr.InvalidAddress) as err:
+ self._log_msg('error', 'send notify to %s failed: %s' %
+ (addr_to_str(addrinfo), str(err)))
return False
return True
diff --git a/src/lib/python/isc/notify/tests/notify_out_test.py b/src/lib/python/isc/notify/tests/notify_out_test.py
index 44725d0..305e38b 100644
--- a/src/lib/python/isc/notify/tests/notify_out_test.py
+++ b/src/lib/python/isc/notify/tests/notify_out_test.py
@@ -24,9 +24,7 @@ from isc.notify import notify_out, SOCK_DATA
# our fake socket, where we can read and insert messages
class MockSocket():
- def __init__(self, family, type):
- self.family = family
- self.type = type
+ def __init__(self):
self._local_sock, self._remote_sock = socket.socketpair()
def connect(self, to):
@@ -51,12 +49,16 @@ class MockSocket():
return self._remote_sock
# We subclass the ZoneNotifyInfo class we're testing here, only
-# to override the prepare_notify_out() method.
+# to override the create_socket() method.
class MockZoneNotifyInfo(notify_out.ZoneNotifyInfo):
- def prepare_notify_out(self):
- super().prepare_notify_out();
+ def create_socket(self, addrinfo):
+ super().create_socket(addrinfo)
+ # before replacing the underlying socket, remember the address family
+ # of the original socket so that tests can check that.
+ self.sock_family = self._sock.family
self._sock.close()
- self._sock = MockSocket(socket.AF_INET, socket.SOCK_DGRAM)
+ self._sock = MockSocket()
+ return self._sock
class TestZoneNotifyInfo(unittest.TestCase):
def setUp(self):
@@ -64,11 +66,12 @@ class TestZoneNotifyInfo(unittest.TestCase):
def test_prepare_finish_notify_out(self):
self.info.prepare_notify_out()
- self.assertNotEqual(self.info._sock, None)
+ self.assertNotEqual(self.info.notify_timeout, None)
self.assertIsNone(self.info._notify_current)
self.info.finish_notify_out()
self.assertEqual(self.info._sock, None)
+ self.assertEqual(self.info.notify_timeout, None)
def test_set_next_notify_target(self):
self.info.notify_slaves.append(('127.0.0.1', 53))
@@ -155,6 +158,11 @@ class TestNotifyOut(unittest.TestCase):
self.assertEqual(len(replied_zones), 0)
self.assertEqual(len(timeout_zones), 2)
+ # Trigger timeout events to "send" notifies via a mock socket
+ for zone in timeout_zones:
+ self._notify._zone_notify_handler(timeout_zones[zone],
+ notify_out._EVENT_TIMEOUT)
+
# Now make one socket be readable
self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() + 10
self._notify._notify_infos[('example.com.', 'IN')].notify_timeout = time.time() + 10
@@ -234,11 +242,31 @@ class TestNotifyOut(unittest.TestCase):
data = b'\x2f\x18\x10\x10\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01'
self.assertEqual(notify_out._BAD_QR, self._notify._handle_notify_reply(example_com_info, data))
- def test_send_notify_message_udp(self):
+ def test_send_notify_message_udp_ipv4(self):
example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
example_com_info.prepare_notify_out()
- ret = self._notify._send_notify_message_udp(example_com_info, ('1.1.1.1', 53))
+ ret = self._notify._send_notify_message_udp(example_com_info,
+ ('192.0.2.1', 53))
+ self.assertTrue(ret)
+ self.assertEqual(socket.AF_INET, example_com_info.sock_family)
+
+ def test_send_notify_message_udp_ipv6(self):
+ example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
+ ret = self._notify._send_notify_message_udp(example_com_info,
+ ('2001:db8::53', 53))
self.assertTrue(ret)
+ self.assertEqual(socket.AF_INET6, example_com_info.sock_family)
+
+ def test_send_notify_message_with_bogus_address(self):
+ example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
+
+ # As long as the underlying data source validates RDATA this shouldn't
+ # happen, but right now it's not actually the case. Even if the
+ # data source does its job, it's prudent to confirm the behavior for
+ # an unexpected case.
+ ret = self._notify._send_notify_message_udp(example_com_info,
+ ('invalid', 53))
+ self.assertFalse(ret)
def test_zone_notify_handler(self):
old_send_msg = self._notify._send_notify_message_udp
diff --git a/src/lib/resolve/Makefile.am b/src/lib/resolve/Makefile.am
index 0b29da4..94ad371 100644
--- a/src/lib/resolve/Makefile.am
+++ b/src/lib/resolve/Makefile.am
@@ -7,16 +7,36 @@ AM_CPPFLAGS += $(SQLITE_CFLAGS)
AM_CXXFLAGS = $(B10_CXXFLAGS)
-CLEANFILES = *.gcno *.gcda
+# Define rule to build logging source files from message file
+resolvedef.h resolvedef.cc: resolvedef.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/resolve/resolvedef.mes
+
+# Tell Automake that the nsasdef.{cc,h} source files are created in the build
+# process, so it must create these before doing anything else. Although they
+# are a dependency of the library (so will be created from the message file
+# anyway), there is no guarantee as to exactly _when_ in the build they will be
+# created. As the .h file is included in other sources file (so must be
+# present when they are compiled), the safest option is to create it first.
+BUILT_SOURCES = resolvedef.h resolvedef.cc
+
+CLEANFILES = *.gcno *.gcda resolvedef.cc resolvedef.h
lib_LTLIBRARIES = libresolve.la
libresolve_la_SOURCES = resolve.h resolve.cc
+libresolve_la_SOURCES += resolve_log.h resolve_log.cc
libresolve_la_SOURCES += resolver_interface.h
libresolve_la_SOURCES += resolver_callback.h resolver_callback.cc
libresolve_la_SOURCES += response_classifier.cc response_classifier.h
libresolve_la_SOURCES += recursive_query.cc recursive_query.h
+
+nodist_libresolve_la_SOURCES = resolvedef.h resolvedef.cc
+
libresolve_la_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la
libresolve_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+libresolve_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la
+
+# The message file should be in the distribution.
+EXTRA_DIST = resolvedef.mes
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc
index b753cc9..ad073ae 100644
--- a/src/lib/resolve/recursive_query.cc
+++ b/src/lib/resolve/recursive_query.cc
@@ -16,14 +16,13 @@
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h> // for some IPC/network system calls
+#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
#include <config.h>
-#include <log/dummylog.h>
-
#include <dns/question.h>
#include <dns/message.h>
#include <dns/opcode.h>
@@ -31,6 +30,7 @@
#include <dns/rdataclass.h>
#include <resolve/resolve.h>
+#include <resolve/resolve_log.h>
#include <cache/resolver_cache.h>
#include <nsas/address_request_callback.h>
#include <nsas/nameserver_address.h>
@@ -41,10 +41,10 @@
#include <asiolink/io_service.h>
#include <resolve/recursive_query.h>
-using isc::log::dlog;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::asiolink;
+using namespace isc::resolve;
namespace isc {
namespace asiodns {
@@ -64,8 +64,20 @@ hasAddress(const Name& name, const RRClass& rrClass,
cache.lookup(name, RRType::AAAA(), rrClass) != RRsetPtr());
}
+// Convenience function for debug messages. Question::toText() includes
+// a trailing newline in its output, which makes it awkward to embed in a
+// message. This just strips that newline from it.
+std::string
+questionText(const isc::dns::Question& question) {
+ std::string text = question.toText();
+ if (!text.empty()) {
+ text.erase(text.size() - 1);
+ }
+ return (text);
}
+} // anonymous namespace
+
/// \brief Find deepest usable delegation in the cache
///
/// This finds the deepest delegation we have in cache and is safe to use.
@@ -135,8 +147,7 @@ RecursiveQuery::RecursiveQuery(DNSService& dns_service,
// Set the test server - only used for unit testing.
void
RecursiveQuery::setTestServer(const std::string& address, uint16_t port) {
- dlog("Setting test server to " + address + "(" +
- boost::lexical_cast<std::string>(port) + ")");
+ LOG_WARN(isc::resolve::logger, RESLIB_TESTSERV).arg(address).arg(port);
test_server_.first = address;
test_server_.second = port;
}
@@ -165,14 +176,16 @@ public:
ResolverNSASCallback(RunningQuery* rq) : rq_(rq) {}
void success(const isc::nsas::NameserverAddress& address) {
- dlog("Found a nameserver, sending query to " + address.getAddress().toText());
+ // Success callback, send query to found namesever
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQUSUCC)
+ .arg(address.getAddress().toText());
rq_->nsasCallbackCalled();
rq_->sendTo(address);
}
void unreachable() {
- dlog("Nameservers unreachable");
- // Drop query or send servfail?
+ // Nameservers unreachable: drop query or send servfail?
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQUFAIL);
rq_->nsasCallbackCalled();
rq_->makeSERVFAIL();
rq_->callCallback(true);
@@ -298,12 +311,16 @@ private:
// if we have a response for our query stored already. if
// so, call handlerecursiveresponse(), if not, we call send()
void doLookup() {
- dlog("doLookup: try cache");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RUNCALOOK)
+ .arg(questionText(question_));
+
Message cached_message(Message::RENDER);
isc::resolve::initResponseMessage(question_, cached_message);
if (cache_.lookup(question_.getName(), question_.getType(),
question_.getClass(), cached_message)) {
- dlog("Message found in cache, continuing with that");
+
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RUNCAFND)
+ .arg(questionText(question_));
// Should these be set by the cache too?
cached_message.setOpcode(Opcode::QUERY());
cached_message.setRcode(Rcode::NOERROR());
@@ -313,9 +330,10 @@ private:
stop();
}
} else {
- dlog("doLookup: get lowest usable delegation from cache");
cur_zone_ = deepestDelegation(question_.getName(),
question_.getClass(), cache_);
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_DEEPEST)
+ .arg(questionText(question_)).arg(cur_zone_);
send();
}
@@ -347,8 +365,9 @@ private:
void send(IOFetch::Protocol protocol = IOFetch::UDP) {
protocol_ = protocol; // Store protocol being used for this
if (test_server_.second != 0) {
- dlog("Sending upstream query (" + question_.toText() +
- ") to test server at " + test_server_.first);
+ // Send query to test server
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_TESTUPSTR)
+ .arg(questionText(question_)).arg(test_server_.first);
gettimeofday(¤t_ns_qsent_time, NULL);
++outstanding_events_;
IOFetch query(protocol, io_, question_,
@@ -356,10 +375,13 @@ private:
test_server_.second, buffer_, this,
query_timeout_);
io_.get_io_service().post(query);
+
} else {
// Ask the NSAS for an address for the current zone,
// the callback will call the actual sendTo()
- dlog("Look up nameserver for " + cur_zone_ + " in NSAS");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_NSASLOOK)
+ .arg(cur_zone_);
+
// Can we have multiple calls to nsas_out? Let's assume not
// for now
assert(!nsas_callback_out_);
@@ -387,7 +409,7 @@ private:
// error message)
// returns false if we are not done
bool handleRecursiveAnswer(const Message& incoming) {
- dlog("Handle response");
+
// In case we get a CNAME, we store the target
// here (classify() will set it when it walks through
// the cname chain to verify it).
@@ -402,46 +424,60 @@ private:
switch (category) {
case isc::resolve::ResponseClassifier::ANSWER:
case isc::resolve::ResponseClassifier::ANSWERCNAME:
- // Done. copy and return.
- dlog("Response is an answer");
+ // Answer received - copy and return.
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_ANSWER)
+ .arg(questionText(question_));
isc::resolve::copyResponseMessage(incoming, answer_message_);
cache_.update(*answer_message_);
return true;
break;
+
case isc::resolve::ResponseClassifier::CNAME:
- dlog("Response is CNAME!");
+ // CNAME received.
+
// (unfinished) CNAME. We set our question_ to the CNAME
// target, then start over at the beginning (for now, that
// is, we reset our 'current servers' to the root servers).
if (cname_count_ >= RESOLVER_MAX_CNAME_CHAIN) {
- // just give up
- dlog("CNAME chain too long");
+ // CNAME chain too long - just give up
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_LONGCHAIN)
+ .arg(questionText(question_));
makeSERVFAIL();
return true;
}
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_CNAME)
+ .arg(questionText(question_));
+
answer_message_->appendSection(Message::SECTION_ANSWER,
incoming);
question_ = Question(cname_target, question_.getClass(),
question_.getType());
- dlog("Following CNAME chain to " + question_.toText());
+ // Follow CNAME chain.
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_FOLLOWCNAME)
+ .arg(questionText(question_));
doLookup();
return false;
break;
+
case isc::resolve::ResponseClassifier::NXDOMAIN:
case isc::resolve::ResponseClassifier::NXRRSET:
- dlog("Response is NXDOMAIN or NXRRSET");
- // NXDOMAIN, just copy and return.
- dlog(incoming.toText());
+ // Received NXDOMAIN or NXRRSET, just copy and return
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_NXDOMRR)
+ .arg(questionText(question_));
isc::resolve::copyResponseMessage(incoming, answer_message_);
// no negcache yet
//cache_.update(*answer_message_);
return true;
break;
+
case isc::resolve::ResponseClassifier::REFERRAL:
- dlog("Response is referral");
+ // Response is a referral
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_REFERRAL)
+ .arg(questionText(question_));
+
cache_.update(incoming);
// Referral. For now we just take the first glue address
// we find and continue with that
@@ -460,7 +496,8 @@ private:
// (this requires a few API changes in related
// libraries, so as not to need many conversions)
cur_zone_ = rrs->getName().toText();
- dlog("Referred to zone " + cur_zone_);
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_REFERZONE)
+ .arg(cur_zone_);
found_ns = true;
break;
}
@@ -484,7 +521,10 @@ private:
nsas_callback_, ANY_OK, glue_hints);
return false;
} else {
- dlog("No NS RRset in referral?");
+ // Referral was received but did not contain an NS RRset.
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_NONSRRSET)
+ .arg(questionText(question_));
+
// TODO this will result in answering with the delegation. oh well
isc::resolve::copyResponseMessage(incoming, answer_message_);
return true;
@@ -494,7 +534,8 @@ private:
// Truncated packet. If the protocol we used for the last one is
// UDP, re-query using TCP. Otherwise regard it as an error.
if (protocol_ == IOFetch::UDP) {
- dlog("Response truncated, re-querying over TCP");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TRUNCATED)
+ .arg(questionText(question_));
send(IOFetch::TCP);
return false;
}
@@ -513,6 +554,8 @@ private:
case isc::resolve::ResponseClassifier::NOTSINGLE:
case isc::resolve::ResponseClassifier::OPCODE:
case isc::resolve::ResponseClassifier::RCODE:
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_RCODERR)
+ .arg(questionText(question_));
// Should we try a different server rather than SERVFAIL?
makeSERVFAIL();
return true;
@@ -677,7 +720,7 @@ public:
rtt = 1000 * (cur_time.tv_sec - current_ns_qsent_time.tv_sec);
rtt += (cur_time.tv_usec - current_ns_qsent_time.tv_usec) / 1000;
}
- dlog("RTT: " + boost::lexical_cast<std::string>(rtt));
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_RTT).arg(rtt);
current_ns_address.updateRTT(rtt);
if (rtt_recorder_) {
rtt_recorder_->addRtt(rtt);
@@ -701,19 +744,22 @@ public:
stop();
}
} catch (const isc::dns::DNSProtocolError& dpe) {
- dlog("DNS Protocol error in answer for " +
- question_.toText() + " " +
- question_.getType().toText() + ": " +
- dpe.what());
// Right now, we treat this similar to timeouts
// (except we don't store RTT)
// We probably want to make this an integral part
// of the fetch data process. (TODO)
if (retries_--) {
- dlog("Retrying");
+ // Retry
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
+ RESLIB_PROTOCOLRTRY)
+ .arg(questionText(question_)).arg(dpe.what())
+ .arg(retries_);
send();
} else {
- dlog("Giving up");
+ // Give up
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
+ RESLIB_PROTOCOL)
+ .arg(questionText(question_)).arg(dpe.what());
if (!callback_called_) {
makeSERVFAIL();
callCallback(true);
@@ -723,13 +769,17 @@ public:
}
} else if (!done_ && retries_--) {
// Query timed out, but we have some retries, so send again
- dlog("Timeout for " + question_.toText() + " to " + current_ns_address.getAddress().toText() + ", resending query");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TIMEOUTRTRY)
+ .arg(questionText(question_))
+ .arg(current_ns_address.getAddress().toText()).arg(retries_);
current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
send();
} else {
// We are either already done, or out of retries
if (result == IOFetch::TIME_OUT) {
- dlog("Timeout for " + question_.toText() + " to " + current_ns_address.getAddress().toText() + ", giving up");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TIMEOUT)
+ .arg(questionText(question_))
+ .arg(current_ns_address.getAddress().toText());
current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
}
if (!callback_called_) {
@@ -793,8 +843,10 @@ private:
buffer_->clear();
int serverIndex = rand() % uc;
ConstQuestionPtr question = *(query_message_->beginQuestion());
- dlog("Sending upstream query (" + question->toText() +
- ") to " + upstream_->at(serverIndex).first);
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_UPSTREAM)
+ .arg(questionText(*question))
+ .arg(upstream_->at(serverIndex).first);
+
++outstanding_events_;
// Forward the query, create the IOFetch with
// query message, so that query flags can be forwarded
@@ -934,14 +986,16 @@ RecursiveQuery::resolve(const QuestionPtr& question,
OutputBufferPtr buffer(new OutputBuffer(0));
- dlog("Asked to resolve: " + question->toText());
-
- dlog("Try out cache first (direct call to resolve)");
// First try to see if we have something cached in the messagecache
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE)
+ .arg(questionText(*question)).arg(1);
if (cache_.lookup(question->getName(), question->getType(),
question->getClass(), *answer_message) &&
answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
- dlog("Message found in cache, returning that");
+ // Message found, return that
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RESCAFND)
+ .arg(questionText(*question)).arg(1);
+
// TODO: err, should cache set rcode as well?
answer_message->setRcode(Rcode::NOERROR());
callback->success(answer_message);
@@ -952,14 +1006,18 @@ RecursiveQuery::resolve(const QuestionPtr& question,
question->getType(),
question->getClass());
if (cached_rrset) {
- dlog("Found single RRset in cache");
+ // Found single RRset in cache
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RRSETFND)
+ .arg(questionText(*question)).arg(1);
answer_message->addRRset(Message::SECTION_ANSWER,
cached_rrset);
answer_message->setRcode(Rcode::NOERROR());
callback->success(answer_message);
} else {
- dlog("Message not found in cache, starting recursive query");
- // It will delete itself when it is done
+ // Message not found in cache, start recursive query. It will
+ // delete itself when it is done
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESCANOTFND)
+ .arg(questionText(*question)).arg(1);
new RunningQuery(io, *question, answer_message,
test_server_, buffer, callback,
query_timeout_, client_timeout_,
@@ -988,14 +1046,17 @@ RecursiveQuery::resolve(const Question& question,
answer_message->setOpcode(isc::dns::Opcode::QUERY());
answer_message->addQuestion(question);
- dlog("Asked to resolve: " + question.toText());
-
// First try to see if we have something cached in the messagecache
- dlog("Try out cache first (started by incoming event)");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE)
+ .arg(questionText(question)).arg(2);
+
if (cache_.lookup(question.getName(), question.getType(),
question.getClass(), *answer_message) &&
answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
- dlog("Message found in cache, returning that");
+
+ // Message found, return that
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RESCAFND)
+ .arg(questionText(question)).arg(2);
// TODO: err, should cache set rcode as well?
answer_message->setRcode(Rcode::NOERROR());
crs->success(answer_message);
@@ -1006,14 +1067,19 @@ RecursiveQuery::resolve(const Question& question,
question.getType(),
question.getClass());
if (cached_rrset) {
- dlog("Found single RRset in cache");
+ // Found single RRset in cache
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RRSETFND)
+ .arg(questionText(question)).arg(2);
answer_message->addRRset(Message::SECTION_ANSWER,
cached_rrset);
answer_message->setRcode(Rcode::NOERROR());
crs->success(answer_message);
+
} else {
- dlog("Message not found in cache, starting recursive query");
- // It will delete itself when it is done
+ // Message not found in cache, start recursive query. It will
+ // delete itself when it is done
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESCANOTFND)
+ .arg(questionText(question)).arg(2);
new RunningQuery(io, question, answer_message,
test_server_, buffer, crs, query_timeout_,
client_timeout_, lookup_timeout_, retries_,
diff --git a/src/lib/resolve/resolve_log.cc b/src/lib/resolve/resolve_log.cc
new file mode 100644
index 0000000..e41d8d2
--- /dev/null
+++ b/src/lib/resolve/resolve_log.cc
@@ -0,0 +1,26 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// Defines the logger used by the NSAS
+
+#include <resolve/resolve_log.h>
+
+namespace isc {
+namespace resolve {
+
+isc::log::Logger logger("reslib"); // Distinct from "resolver"
+
+} // namespace resolve
+} // namespace isc
+
diff --git a/src/lib/resolve/resolve_log.h b/src/lib/resolve/resolve_log.h
new file mode 100644
index 0000000..89d23c6
--- /dev/null
+++ b/src/lib/resolve/resolve_log.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __RESOLVE_LOG__H
+#define __RESOLVE_LOG__H
+
+#include <log/macros.h>
+#include "resolvedef.h"
+
+namespace isc {
+namespace resolve {
+
+/// \brief Resolver Library Logging
+///
+/// Defines the levels used to output debug messages in the resolver library.
+/// Note that higher numbers equate to more verbose (and detailed) output.
+
+// The first level traces normal operations
+const int RESLIB_DBG_TRACE = 10;
+
+// The next level extends the normal operations and records the results of the
+// lookups.
+const int RESLIB_DBG_RESULTS = 20;
+
+// Report cache lookups and results
+const int RESLIB_DBG_CACHE = 40;
+
+// Indicate when callbacks are called
+const int RESLIB_DBG_CB = 50;
+
+
+/// \brief Resolver Library Logger
+///
+/// Define the logger used to log messages. We could define it in multiple
+/// modules, but defining in a single module and linking to it saves time and
+/// space.
+extern isc::log::Logger logger;
+
+} // namespace resolve
+} // namespace isc
+
+#endif // __RESOLVE_LOG__H
diff --git a/src/lib/resolve/resolvedef.mes b/src/lib/resolve/resolvedef.mes
new file mode 100644
index 0000000..61870a6
--- /dev/null
+++ b/src/lib/resolve/resolvedef.mes
@@ -0,0 +1,155 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$PREFIX RESLIB_
+$NAMESPACE isc::resolve
+
+% ANSWER answer received in response to query for <%1>
+A debug message recording that an answer has been received to an upstream
+query for the specified question. Previous debug messages will have indicated
+the server to which the question was sent.
+
+% CNAME CNAME received in response to query for <%1>
+A debug message recording that CNAME response has been received to an upstream
+query for the specified question. Previous debug messages will have indicated
+the server to which the question was sent.
+
+% DEEPEST did not find <%1> in cache, deepest delegation found is %2
+A debug message, a cache lookup did not find the specified <name, class,
+type> tuple in the cache; instead, the deepest delegation found is indicated.
+
+% FOLLOWCNAME following CNAME chain to <%1>
+A debug message, a CNAME response was received and another query is being issued
+for the <name, class, type> tuple.
+
+% LONGCHAIN CNAME received in response to query for <%1>: CNAME chain length exceeded
+A debug message recording that a CNAME response has been received to an upstream
+query for the specified question (Previous debug messages will have indicated
+the server to which the question was sent). However, receipt of this CNAME
+has meant that the resolver has exceeded the CNAME chain limit (a CNAME chain
+is where on CNAME points to another) and so an error is being returned.
+
+% NONSRRSET no NS RRSet in referral response received to query for <%1>
+A debug message, this indicates that a response was received for the specified
+query and was categorised as a referral. However, the received message did
+not contain any NS RRsets. This may indicate a programming error in the
+response classification code.
+
+% NSASLOOK looking up nameserver for zone %1 in the NSAS
+A debug message, the RunningQuery object is querying the NSAS for the
+nameservers for the specified zone.
+
+% NXDOMRR NXDOMAIN/NXRRSET received in response to query for <%1>
+A debug message recording that either a NXDOMAIN or an NXRRSET response has
+been received to an upstream query for the specified question. Previous debug
+messages will have indicated the server to which the question was sent.
+
+% PROTOCOL protocol error in answer for %1: %3
+A debug message indicating that a protocol error was received. As there
+are no retries left, an error will be reported.
+
+% PROTOCOLRTRY protocol error in answer for %1: %2 (retries left: %3)
+A debug message indicating that a protocol error was received and that
+the resolver is repeating the query to the same nameserver. After this
+repeated query, there will be the indicated number of retries left.
+
+% RCODERR RCODE indicates error in response to query for <%1>
+A debug message, the response to the specified query indicated an error
+that is not covered by a specific code path. A SERVFAIL will be returned.
+
+% REFERRAL referral received in response to query for <%1>
+A debug message recording that a referral response has been received to an
+upstream query for the specified question. Previous debug messages will
+have indicated the server to which the question was sent.
+
+% REFERZONE referred to zone %1
+A debug message indicating that the last referral message was to the specified
+zone.
+
+% RESCAFND found <%1> in the cache (resolve() instance %2)
+This is a debug message and indicates that a RecursiveQuery object found the
+the specified <name, class, type> tuple in the cache. The instance number
+at the end of the message indicates which of the two resolve() methods has
+been called.
+
+% RESCANOTFND did not find <%1> in the cache, starting RunningQuery (resolve() instance %2)
+This is a debug message and indicates that the look in the cache made by the
+RecursiveQuery::resolve() method did not find an answer, so a new RunningQuery
+object has been created to resolve the question. The instance number at
+the end of the message indicates which of the two resolve() methods has
+been called.
+
+% RESOLVE asked to resolve <%1> (resolve() instance %2)
+A debug message, the RecursiveQuery::resolve method has been called to resolve
+the specified <name, class, type> tuple. The first action will be to lookup
+the specified tuple in the cache. The instance number at the end of the
+message indicates which of the two resolve() methods has been called.
+
+% RRSETFND found single RRset in the cache when querying for <%1> (resolve() instance %2)
+A debug message, indicating that when RecursiveQuery::resolve queried the
+cache, a single RRset was found which was put in the answer. The instance
+number at the end of the message indicates which of the two resolve()
+methods has been called.
+
+% RTT round-trip time of last query calculated as %1 ms
+A debug message giving the round-trip time of the last query and response.
+
+% RUNCAFND found <%1> in the cache
+This is a debug message and indicates that a RunningQuery object found
+the specified <name, class, type> tuple in the cache.
+
+% RUNCALOOK looking up up <%1> in the cache
+This is a debug message and indicates that a RunningQuery object has made
+a call to its doLookup() method to look up the specified <name, class, type>
+tuple, the first action of which will be to examine the cache.
+
+% RUNQUFAIL failure callback - nameservers are unreachable
+A debug message indicating that a RunningQuery's failure callback has been
+called because all nameservers for the zone in question are unreachable.
+
+% RUNQUSUCC success callback - sending query to %1
+A debug message indicating that a RunningQuery's success callback has been
+called because a nameserver has been found, and that a query is being sent
+to the specified nameserver.
+
+% TESTSERV setting test server to %1(%2)
+This is an internal debugging message and is only generated in unit tests.
+It indicates that all upstream queries from the resolver are being routed to
+the specified server, regardless of the address of the nameserver to which
+the query would normally be routed. As it should never be seen in normal
+operation, it is a warning message instead of a debug message.
+
+% TESTUPSTR sending upstream query for <%1> to test server at %2
+This is a debug message and should only be seen in unit tests. A query for
+the specified <name, class, type> tuple is being sent to a test nameserver
+whose address is given in the message.
+
+% TIMEOUT query <%1> to %2 timed out
+A debug message indicating that the specified query has timed out and as
+there are no retries left, an error will be reported.
+
+% TIMEOUTRTRY query <%1> to %2 timed out, re-trying (retries left: %3)
+A debug message indicating that the specified query has timed out and that
+the resolver is repeating the query to the same nameserver. After this
+repeated query, there will be the indicated number of retries left.
+
+% TRUNCATED response to query for <%1> was truncated, re-querying over TCP
+A debug message, this indicates that the response to the specified query was
+truncated and that the resolver will be re-querying over TCP. There are
+various reasons why responses may be truncated, so this message is normal and
+gives no cause for concern.
+
+% UPSTREAM sending upstream query for <%1> to %2
+A debug message indicating that a query for the specified <name, class, type>
+tuple is being sent to a nameserver whose address is given in the message.
diff --git a/src/lib/resolve/tests/Makefile.am b/src/lib/resolve/tests/Makefile.am
index edea7cd..ee311a6 100644
--- a/src/lib/resolve/tests/Makefile.am
+++ b/src/lib/resolve/tests/Makefile.am
@@ -11,9 +11,11 @@ CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
TESTS += run_unittests
+
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_SOURCES = run_unittests.cc
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += resolve_unittest.cc
@@ -23,7 +25,6 @@ run_unittests_SOURCES += recursive_query_unittest.cc
run_unittests_SOURCES += recursive_query_unittest_2.cc
run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
@@ -31,6 +32,8 @@ run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/resolve/tests/run_unittests.cc b/src/lib/resolve/tests/run_unittests.cc
index f80e167..fe8124e 100644
--- a/src/lib/resolve/tests/run_unittests.cc
+++ b/src/lib/resolve/tests/run_unittests.cc
@@ -13,12 +13,15 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
+#include <log/logger_support.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
+ isc::log::initLogger();
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/server_common/keyring.cc b/src/lib/server_common/keyring.cc
index f68db70..b60e796 100644
--- a/src/lib/server_common/keyring.cc
+++ b/src/lib/server_common/keyring.cc
@@ -27,10 +27,16 @@ KeyringPtr keyring;
namespace {
void
-updateKeyring(const std::string&, ConstElementPtr data) {
+updateKeyring(const std::string&, ConstElementPtr data,
+ const isc::config::ConfigData&) {
ConstElementPtr list(data->get("keys"));
KeyringPtr load(new TSIGKeyRing);
- for (size_t i(0); i < list->size(); ++ i) {
+
+ // Note that 'data' only contains explicitly configured config parameters.
+ // So if we use the default list is NULL, rather than an empty list, and
+ // we must explicitly expect that case (and handle it just like an empty
+ // list).
+ for (size_t i(0); list && i < list->size(); ++ i) {
load->add(TSIGKey(list->get(i)->stringValue()));
}
keyring.swap(load);
diff --git a/src/lib/server_common/keyring.h b/src/lib/server_common/keyring.h
index 8832095..9c067e9 100644
--- a/src/lib/server_common/keyring.h
+++ b/src/lib/server_common/keyring.h
@@ -28,14 +28,20 @@
* on updates.
*
* You simply initialize/load the keyring with isc::server_common::initKeyring
- * and then just use the key ring in in isc::server_common::keyring. It is
- * automatically reloaded, when the configuration updates, so you no longer
+ * and then just use the key ring referred to by isc::server_common::keyring. It
+ * is automatically reloaded, when the configuration updates, so you no longer
* needs to care about it.
*
* If you want to keep a key (or session) for longer time or your application
- * is multithreaded, you might want to have a copy of the shared pointer.
- * Otherwise an update might replace the keyring and delete the keys in the
- * old one.
+ * is multithreaded, you might want to have a copy of the shared pointer to
+ * hold a reference. Otherwise an update might replace the keyring and delete
+ * the keys in the old one.
+ *
+ * Also note that, while the interface doesn't prevent application from
+ * modifying the keyring, it is not a good idea to do so. As mentioned above,
+ * it might get reloaded at any time, which would replace the modified keyring.
+ * The possibility to modify it is side effect of simpler implementation and
+ * shorter code, not a goal.
*/
namespace isc {
diff --git a/src/lib/server_common/tests/Makefile.am b/src/lib/server_common/tests/Makefile.am
index cecb98b..ecdb2d9 100644
--- a/src/lib/server_common/tests/Makefile.am
+++ b/src/lib/server_common/tests/Makefile.am
@@ -33,13 +33,13 @@ nodist_run_unittests_SOURCES = data_path.h
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
-
run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
run_unittests_LDADD += $(top_builddir)/src/lib/config/tests/libfake_session.la
endif
diff --git a/src/lib/server_common/tests/keyring_test.cc b/src/lib/server_common/tests/keyring_test.cc
index 6d2f226..d79b541 100644
--- a/src/lib/server_common/tests/keyring_test.cc
+++ b/src/lib/server_common/tests/keyring_test.cc
@@ -38,20 +38,29 @@ public:
specfile(std::string(TEST_DATA_PATH) + "/spec.spec")
{
session.getMessages()->add(createAnswer());
- mccs.reset(new ModuleCCSession(specfile, session, NULL, NULL));
+ mccs.reset(new ModuleCCSession(specfile, session, NULL, NULL, false));
}
isc::cc::FakeSession session;
std::auto_ptr<ModuleCCSession> mccs;
std::string specfile;
- void doInit() {
+ void doInit(bool with_key = true) {
// Prepare the module specification for it and the config
session.getMessages()->
add(createAnswer(0,
moduleSpecFromFile(std::string(PLUGIN_DATA_PATH) +
"/tsig_keys.spec").
getFullSpec()));
- session.getMessages()->add(createAnswer(0, Element::fromJSON(
- "{\"keys\": [\"key:MTIzNAo=:hmac-sha1\"]}")));
+ if (with_key) {
+ session.getMessages()->add(
+ createAnswer(0, Element::fromJSON(
+ "{\"keys\": [\"key:MTIzNAo=:hmac-sha1\"]}")));
+ } else {
+ // This emulates the case of using the spec default. Note that
+ // the default value won't be passed to the config handler, so
+ // we'll pass an empty object, instead of {"keys": []}.
+ session.getMessages()->add(createAnswer(0,
+ Element::fromJSON("{}")));
+ }
// Now load it
EXPECT_NO_THROW(initKeyring(*mccs));
EXPECT_NE(keyring, boost::shared_ptr<TSIGKeyRing>()) <<
@@ -97,6 +106,14 @@ TEST_F(KeyringTest, keyring) {
}
}
+TEST_F(KeyringTest, keyringWithDefault) {
+ // If we don't explicitly specify a keyring, the default (no key) will
+ // be used.
+ doInit(false);
+ EXPECT_EQ(0, keyring->size());
+ deinitKeyring(*mccs);
+}
+
// Init twice
TEST_F(KeyringTest, initTwice) {
// It is NULL before
diff --git a/src/lib/server_common/tests/run_unittests.cc b/src/lib/server_common/tests/run_unittests.cc
index 7ebc985..b982ef3 100644
--- a/src/lib/server_common/tests/run_unittests.cc
+++ b/src/lib/server_common/tests/run_unittests.cc
@@ -15,6 +15,7 @@
#include <config.h>
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
@@ -22,5 +23,5 @@ int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/testutils/srv_test.cc b/src/lib/testutils/srv_test.cc
index 1d79d71..dd3e425 100644
--- a/src/lib/testutils/srv_test.cc
+++ b/src/lib/testutils/srv_test.cc
@@ -72,9 +72,13 @@ SrvTestBase::createDataFromFile(const char* const datafile,
void
SrvTestBase::createRequestPacket(Message& message,
- const int protocol)
+ const int protocol, TSIGContext* context)
{
- message.toWire(request_renderer);
+ if (context == NULL) {
+ message.toWire(request_renderer);
+ } else {
+ message.toWire(request_renderer, *context);
+ }
delete io_message;
diff --git a/src/lib/testutils/srv_test.h b/src/lib/testutils/srv_test.h
index a848ffc..c92e876 100644
--- a/src/lib/testutils/srv_test.h
+++ b/src/lib/testutils/srv_test.h
@@ -84,7 +84,8 @@ protected:
/// form of \c IOMessage in \c io_message.
/// The existing content of \c io_message, if any, will be deleted.
void createRequestPacket(isc::dns::Message& message,
- const int protocol = IPPROTO_UDP);
+ const int protocol = IPPROTO_UDP,
+ isc::dns::TSIGContext* context = NULL);
MockSession notify_session;
MockServer dnsserv;
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
index 3cb1229..3db9ac4 100644
--- a/src/lib/util/Makefile.am
+++ b/src/lib/util/Makefile.am
@@ -1,6 +1,4 @@
-SUBDIRS = . tests unittests io pyunittests
-# The io/tests is hack, because otherwise we can not order these directories
-# properly. Unittests use io and io/tests use unittest.
+SUBDIRS = . io unittests tests pyunittests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
diff --git a/src/lib/util/encode/base_n.cc b/src/lib/util/encode/base_n.cc
index 406dc77..0026a0b 100644
--- a/src/lib/util/encode/base_n.cc
+++ b/src/lib/util/encode/base_n.cc
@@ -160,19 +160,42 @@ public:
base_zero_code_(base_zero_code),
base_(base), base_beginpad_(base_beginpad), base_end_(base_end),
in_pad_(false)
- {}
+ {
+ // Skip beginning spaces, if any. We need do it here because
+ // otherwise the first call to operator*() would be confused.
+ skipSpaces();
+ }
DecodeNormalizer& operator++() {
++base_;
- while (base_ != base_end_ && isspace(*base_)) {
- ++base_;
- }
+ skipSpaces();
if (base_ == base_beginpad_) {
in_pad_ = true;
}
return (*this);
}
+ void skipSpaces() {
+ // If (char is signed and) *base_ < 0, on Windows platform with Visual
+ // Studio compiler it may trigger _ASSERTE((unsigned)(c + 1) <= 256);
+ // so make sure that the parameter of isspace() is larger than 0.
+ // We don't simply cast it to unsigned char to avoid confusing the
+ // isspace() implementation with a possible extension for values
+ // larger than 127. Also note the check is not ">= 0"; for systems
+ // where char is unsigned that would always be true and would possibly
+ // trigger a compiler warning that could stop the build.
+ while (base_ != base_end_ && *base_ > 0 && isspace(*base_)) {
+ ++base_;
+ }
+ }
const char& operator*() const {
- if (in_pad_ && *base_ == BASE_PADDING_CHAR) {
+ if (base_ == base_end_) {
+ // binary_from_baseX calls this operator when it needs more bits
+ // even if the internal iterator (base_) has reached its end
+ // (if that happens it means the input is an incomplete baseX
+ // string and should be rejected). So this is the only point
+ // we can catch and reject this type of invalid input.
+ isc_throw(BadValue, "Unexpected end of input in BASE decoder");
+ }
+ if (in_pad_) {
return (base_zero_code_);
} else {
return (*base_);
@@ -268,7 +291,7 @@ BaseNTransformer<BitsPerChunk, BaseZeroCode, Encoder, Decoder>::decode(
isc_throw(BadValue, "Too many " << algorithm
<< " padding characters: " << input);
}
- } else if (!isspace(ch)) {
+ } else if (ch < 0 || !isspace(ch)) {
break;
}
++srit;
diff --git a/src/lib/util/io/Makefile.am b/src/lib/util/io/Makefile.am
index 9f06ef9..b2653d8 100644
--- a/src/lib/util/io/Makefile.am
+++ b/src/lib/util/io/Makefile.am
@@ -1,5 +1,3 @@
-SUBDIRS = . tests
-
AM_CXXFLAGS = $(B10_CXXFLAGS)
lib_LTLIBRARIES = libutil_io.la
diff --git a/src/lib/util/io/tests/Makefile.am b/src/lib/util/io/tests/Makefile.am
deleted file mode 100644
index 56d50cf..0000000
--- a/src/lib/util/io/tests/Makefile.am
+++ /dev/null
@@ -1,25 +0,0 @@
-CLEANFILES = *.gcno *.gcda
-
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CXXFLAGS = $(B10_CXXFLAGS)
-
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
-TESTS =
-if HAVE_GTEST
-TESTS += run_unittests
-run_unittests_SOURCES = run_unittests.cc
-run_unittests_SOURCES += fd_tests.cc
-run_unittests_SOURCES += fd_share_tests.cc
-
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
-run_unittests_LDADD += \
- $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-endif
-
-noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/util/io/tests/fd_share_tests.cc b/src/lib/util/io/tests/fd_share_tests.cc
deleted file mode 100644
index 0902ce0..0000000
--- a/src/lib/util/io/tests/fd_share_tests.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include "../fd.h"
-#include "../fd_share.h"
-
-#include <util/unittests/fork.h>
-
-#include <gtest/gtest.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <cstdio>
-
-using namespace isc::util::io;
-using namespace isc::util::unittests;
-
-namespace {
-
-// We test that we can transfer a pipe over other pipe
-TEST(FDShare, transfer) {
- // Get a pipe and fork
- int pipes[2];
- ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, pipes));
- pid_t sender(fork());
- ASSERT_NE(-1, sender);
- if(sender) { // We are in parent
- // Close the other side of pipe, we want only writible one
- EXPECT_NE(-1, close(pipes[0]));
- // Get a process to check data
- int fd(0);
- pid_t checker(check_output(&fd, "data", 4));
- ASSERT_NE(-1, checker);
- // Now, send the file descriptor, close it and close the pipe
- EXPECT_NE(-1, send_fd(pipes[1], fd));
- EXPECT_NE(-1, close(pipes[1]));
- EXPECT_NE(-1, close(fd));
- // Check both subprocesses ended well
- EXPECT_TRUE(process_ok(sender));
- EXPECT_TRUE(process_ok(checker));
- } else { // We are in child. We do not use ASSERT here
- // Close the write end, we only read
- if(close(pipes[1])) {
- exit(1);
- }
- // Get the file descriptor
- int fd(recv_fd(pipes[0]));
- if(fd == -1) {
- exit(1);
- }
- // This pipe is not needed
- if(close(pipes[0])) {
- exit(1);
- }
- // Send "data" trough the received fd, close it and be done
- if(!write_data(fd, "data", 4) || close(fd) == -1) {
- exit(1);
- }
- exit(0);
- }
-}
-
-}
diff --git a/src/lib/util/io/tests/fd_tests.cc b/src/lib/util/io/tests/fd_tests.cc
deleted file mode 100644
index 12b70d8..0000000
--- a/src/lib/util/io/tests/fd_tests.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include "../fd.h"
-
-#include <util/unittests/fork.h>
-
-#include <gtest/gtest.h>
-
-using namespace isc::util::io;
-using namespace isc::util::unittests;
-
-namespace {
-
-// Make sure the test is large enough and does not fit into one
-// read or write request
-const size_t TEST_DATA_SIZE = 8 * 1024 * 1024;
-
-class FDTest : public ::testing::Test {
- public:
- unsigned char *data, *buffer;
- FDTest() :
- // We do not care what is inside, we just need it to be the same
- data(new unsigned char[TEST_DATA_SIZE]),
- buffer(NULL)
- { }
- ~ FDTest() {
- delete[] data;
- delete[] buffer;
- }
-};
-
-// Test we read what was sent
-TEST_F(FDTest, read) {
- int read_pipe(0);
- buffer = new unsigned char[TEST_DATA_SIZE];
- pid_t feeder(provide_input(&read_pipe, data, TEST_DATA_SIZE));
- ASSERT_GE(feeder, 0);
- ssize_t received(read_data(read_pipe, buffer, TEST_DATA_SIZE));
- EXPECT_TRUE(process_ok(feeder));
- EXPECT_EQ(TEST_DATA_SIZE, received);
- EXPECT_EQ(0, memcmp(data, buffer, received));
-}
-
-// Test we write the correct thing
-TEST_F(FDTest, write) {
- int write_pipe(0);
- pid_t checker(check_output(&write_pipe, data, TEST_DATA_SIZE));
- ASSERT_GE(checker, 0);
- EXPECT_TRUE(write_data(write_pipe, data, TEST_DATA_SIZE));
- EXPECT_EQ(0, close(write_pipe));
- EXPECT_TRUE(process_ok(checker));
-}
-
-}
diff --git a/src/lib/util/io/tests/run_unittests.cc b/src/lib/util/io/tests/run_unittests.cc
deleted file mode 100644
index e787ab1..0000000
--- a/src/lib/util/io/tests/run_unittests.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <gtest/gtest.h>
-
-int
-main(int argc, char *argv[]) {
- ::testing::InitGoogleTest(&argc, argv);
-
- return RUN_ALL_TESTS();
-}
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
index 7b97202..47243f8 100644
--- a/src/lib/util/tests/Makefile.am
+++ b/src/lib/util/tests/Makefile.am
@@ -1,8 +1,6 @@
SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/exceptions -I$(top_builddir)/src/lib/exceptions
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -15,26 +13,30 @@ CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
TESTS += run_unittests
-run_unittests_SOURCES =
-run_unittests_SOURCES += filename_unittest.cc
-run_unittests_SOURCES += strutil_unittest.cc
-run_unittests_SOURCES += run_unittests.cc
+run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += base32hex_unittest.cc
run_unittests_SOURCES += base64_unittest.cc
-run_unittests_SOURCES += hex_unittest.cc
-run_unittests_SOURCES += sha1_unittest.cc
run_unittests_SOURCES += buffer_unittest.cc
-run_unittests_SOURCES += time_utilities_unittest.cc
-run_unittests_SOURCES += random_number_generator_unittest.cc
-run_unittests_SOURCES += lru_list_unittest.cc
+run_unittests_SOURCES += fd_share_tests.cc
+run_unittests_SOURCES += fd_tests.cc
+run_unittests_SOURCES += filename_unittest.cc
+run_unittests_SOURCES += hex_unittest.cc
run_unittests_SOURCES += io_utilities_unittest.cc
+run_unittests_SOURCES += lru_list_unittest.cc
run_unittests_SOURCES += qid_gen_unittest.cc
+run_unittests_SOURCES += random_number_generator_unittest.cc
+run_unittests_SOURCES += sha1_unittest.cc
+run_unittests_SOURCES += strutil_unittest.cc
+run_unittests_SOURCES += time_utilities_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
+run_unittests_LDADD += \
+ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/util/tests/base32hex_unittest.cc b/src/lib/util/tests/base32hex_unittest.cc
index bf79627..fa4a290 100644
--- a/src/lib/util/tests/base32hex_unittest.cc
+++ b/src/lib/util/tests/base32hex_unittest.cc
@@ -66,7 +66,7 @@ decodeCheck(const string& input_string, vector<uint8_t>& output,
const string& expected)
{
decodeBase32Hex(input_string, output);
- EXPECT_EQ(expected, string(&output[0], &output[0] + output.size()));
+ EXPECT_EQ(expected, string(output.begin(), output.end()));
}
TEST_F(Base32HexTest, decode) {
@@ -79,6 +79,11 @@ TEST_F(Base32HexTest, decode) {
// whitespace should be allowed
decodeCheck("CP NM\tUOG=", decoded_data, "foob");
decodeCheck("CPNMU===\n", decoded_data, "foo");
+ decodeCheck(" CP NM\tUOG=", decoded_data, "foob");
+ decodeCheck(" ", decoded_data, "");
+
+ // Incomplete input
+ EXPECT_THROW(decodeBase32Hex("CPNMUOJ", decoded_data), BadValue);
// invalid number of padding characters
EXPECT_THROW(decodeBase32Hex("CPNMU0==", decoded_data), BadValue);
diff --git a/src/lib/util/tests/base64_unittest.cc b/src/lib/util/tests/base64_unittest.cc
index c2b2785..b0c926d 100644
--- a/src/lib/util/tests/base64_unittest.cc
+++ b/src/lib/util/tests/base64_unittest.cc
@@ -52,7 +52,7 @@ decodeCheck(const string& input_string, vector<uint8_t>& output,
const string& expected)
{
decodeBase64(input_string, output);
- EXPECT_EQ(expected, string(&output[0], &output[0] + output.size()));
+ EXPECT_EQ(expected, string(output.begin(), output.end()));
}
TEST_F(Base64Test, decode) {
@@ -66,6 +66,12 @@ TEST_F(Base64Test, decode) {
decodeCheck("Zm 9v\tYmF\ny", decoded_data, "foobar");
decodeCheck("Zm9vYg==", decoded_data, "foob");
decodeCheck("Zm9vYmE=\n", decoded_data, "fooba");
+ decodeCheck(" Zm9vYmE=\n", decoded_data, "fooba");
+ decodeCheck(" ", decoded_data, "");
+ decodeCheck("\n\t", decoded_data, "");
+
+ // incomplete input
+ EXPECT_THROW(decodeBase64("Zm9vYmF", decoded_data), BadValue);
// only up to 2 padding characters are allowed
EXPECT_THROW(decodeBase64("A===", decoded_data), BadValue);
diff --git a/src/lib/util/tests/fd_share_tests.cc b/src/lib/util/tests/fd_share_tests.cc
new file mode 100644
index 0000000..cc92e47
--- /dev/null
+++ b/src/lib/util/tests/fd_share_tests.cc
@@ -0,0 +1,74 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/io/fd.h>
+#include <util/io/fd_share.h>
+
+#include <util/unittests/fork.h>
+
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <cstdio>
+
+using namespace isc::util::io;
+using namespace isc::util::unittests;
+
+namespace {
+
+// We test that we can transfer a pipe over other pipe
+TEST(FDShare, transfer) {
+ // Get a pipe and fork
+ int pipes[2];
+ ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, pipes));
+ pid_t sender(fork());
+ ASSERT_NE(-1, sender);
+ if(sender) { // We are in parent
+ // Close the other side of pipe, we want only writible one
+ EXPECT_NE(-1, close(pipes[0]));
+ // Get a process to check data
+ int fd(0);
+ pid_t checker(check_output(&fd, "data", 4));
+ ASSERT_NE(-1, checker);
+ // Now, send the file descriptor, close it and close the pipe
+ EXPECT_NE(-1, send_fd(pipes[1], fd));
+ EXPECT_NE(-1, close(pipes[1]));
+ EXPECT_NE(-1, close(fd));
+ // Check both subprocesses ended well
+ EXPECT_TRUE(process_ok(sender));
+ EXPECT_TRUE(process_ok(checker));
+ } else { // We are in child. We do not use ASSERT here
+ // Close the write end, we only read
+ if(close(pipes[1])) {
+ exit(1);
+ }
+ // Get the file descriptor
+ int fd(recv_fd(pipes[0]));
+ if(fd == -1) {
+ exit(1);
+ }
+ // This pipe is not needed
+ if(close(pipes[0])) {
+ exit(1);
+ }
+ // Send "data" trough the received fd, close it and be done
+ if(!write_data(fd, "data", 4) || close(fd) == -1) {
+ exit(1);
+ }
+ exit(0);
+ }
+}
+
+}
diff --git a/src/lib/util/tests/fd_tests.cc b/src/lib/util/tests/fd_tests.cc
new file mode 100644
index 0000000..6ba2766
--- /dev/null
+++ b/src/lib/util/tests/fd_tests.cc
@@ -0,0 +1,66 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/io/fd.h>
+
+#include <util/unittests/fork.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::util::io;
+using namespace isc::util::unittests;
+
+namespace {
+
+// Make sure the test is large enough and does not fit into one
+// read or write request
+const size_t TEST_DATA_SIZE = 8 * 1024 * 1024;
+
+class FDTest : public ::testing::Test {
+ public:
+ unsigned char *data, *buffer;
+ FDTest() :
+ // We do not care what is inside, we just need it to be the same
+ data(new unsigned char[TEST_DATA_SIZE]),
+ buffer(NULL)
+ { }
+ ~ FDTest() {
+ delete[] data;
+ delete[] buffer;
+ }
+};
+
+// Test we read what was sent
+TEST_F(FDTest, read) {
+ int read_pipe(0);
+ buffer = new unsigned char[TEST_DATA_SIZE];
+ pid_t feeder(provide_input(&read_pipe, data, TEST_DATA_SIZE));
+ ASSERT_GE(feeder, 0);
+ ssize_t received(read_data(read_pipe, buffer, TEST_DATA_SIZE));
+ EXPECT_TRUE(process_ok(feeder));
+ EXPECT_EQ(TEST_DATA_SIZE, received);
+ EXPECT_EQ(0, memcmp(data, buffer, received));
+}
+
+// Test we write the correct thing
+TEST_F(FDTest, write) {
+ int write_pipe(0);
+ pid_t checker(check_output(&write_pipe, data, TEST_DATA_SIZE));
+ ASSERT_GE(checker, 0);
+ EXPECT_TRUE(write_data(write_pipe, data, TEST_DATA_SIZE));
+ EXPECT_EQ(0, close(write_pipe));
+ EXPECT_TRUE(process_ok(checker));
+}
+
+}
diff --git a/src/lib/util/tests/run_unittests.cc b/src/lib/util/tests/run_unittests.cc
index bd3c4c9..a2181cf 100644
--- a/src/lib/util/tests/run_unittests.cc
+++ b/src/lib/util/tests/run_unittests.cc
@@ -13,9 +13,11 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/util/unittests/Makefile.am b/src/lib/util/unittests/Makefile.am
index 340cd1f..83235f2 100644
--- a/src/lib/util/unittests/Makefile.am
+++ b/src/lib/util/unittests/Makefile.am
@@ -5,6 +5,18 @@ lib_LTLIBRARIES = libutil_unittests.la
libutil_unittests_la_SOURCES = fork.h fork.cc resolver.h
libutil_unittests_la_SOURCES += newhook.h newhook.cc
libutil_unittests_la_SOURCES += testdata.h testdata.cc
+if HAVE_GTEST
+libutil_unittests_la_SOURCES += run_all.h run_all.cc
libutil_unittests_la_SOURCES += textdata.h
+endif
+
+libutil_unittests_la_CPPFLAGS = $(AM_CPPFLAGS)
+if HAVE_GTEST
+libutil_unittests_la_CPPFLAGS += $(GTEST_INCLUDES)
+endif
+
+libutil_unittests_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
+libutil_unittests_la_LIBADD += $(top_builddir)/src/lib/util/io/libutil_io.la
+libutil_unittests_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/unittests/run_all.cc b/src/lib/util/unittests/run_all.cc
new file mode 100644
index 0000000..5f50f77
--- /dev/null
+++ b/src/lib/util/unittests/run_all.cc
@@ -0,0 +1,95 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdlib.h>
+
+#include <iostream>
+#include <iomanip>
+
+#include <gtest/gtest.h>
+#include <exceptions/exceptions.h>
+#include <util/unittests/run_all.h>
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+int
+run_all() {
+ int ret = 0;
+
+ // The catching of exceptions generated in tests is controlled by the
+ // B10TEST_CATCH_EXCEPTION environment variable. Setting this to
+ // 1 enables the cacthing of exceptions; setting it to 0 disables it.
+ // Anything else causes a message to be printed to stderr and the default
+ // taken. (The default is to catch exceptions if compiling with clang
+ // and false if not.)
+#ifdef __clang__
+ bool catch_exception = true;
+#else
+ bool catch_exception = false;
+#endif
+
+ const char* b10test_catch_exception = getenv("B10TEST_CATCH_EXCEPTION");
+ if (b10test_catch_exception != NULL) {
+ if (strcmp(b10test_catch_exception, "1") == 0) {
+ catch_exception = true;
+ } else if (strcmp(b10test_catch_exception, "0") == 0) {
+ catch_exception = false;
+ } else {
+ std::cerr << "***ERROR: B10TEST_CATCH_EXCEPTION is '"
+ << b10test_catch_exception
+ << "': allowed values are '1' or '0'.\n"
+ << " The default value of "
+ << (catch_exception ?
+ "1 (exception catching enabled)":
+ "0 (exception catching disabled)")
+ << " will be used.\n";
+ }
+ }
+
+ // Actually run the code
+ if (catch_exception) {
+ try {
+ ret = RUN_ALL_TESTS();
+ } catch (const isc::Exception& ex) {
+ // Could output more information with typeid(), but there is no
+ // guarantee that all compilers will support it without an explicit
+ // flag on the command line.
+ std::cerr << "*** Exception derived from isc::exception thrown:\n"
+ << " file: " << ex.getFile() << "\n"
+ << " line: " << ex.getLine() << "\n"
+ << " what: " << ex.what() << std::endl;
+ throw;
+ } catch (const std::exception& ex) {
+ std::cerr << "*** Exception derived from std::exception thrown:\n"
+ << " what: " << ex.what() << std::endl;
+ throw;
+ }
+ } else {
+ // This is a separate path for the case where the exception is not
+ // being caught. Although the other code path re-throws the exception
+ // after catching it, there is no guarantee that the state of the
+ // stack is preserved - a compiler might have unwound the stack to
+ // the point at which the exception is caught. This would prove
+ // awkward if trying to debug the program using a debugger.
+ ret = RUN_ALL_TESTS();
+ }
+
+ return (ret);
+}
+
+} // namespace unittests
+} // namespace util
+} // namespace isc
diff --git a/src/lib/util/unittests/run_all.h b/src/lib/util/unittests/run_all.h
new file mode 100644
index 0000000..94c7cb0
--- /dev/null
+++ b/src/lib/util/unittests/run_all.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+
+
+#ifndef __RUN_ALL_H
+#define __RUN_ALL_H
+
+// Avoid need for user to include this header file.
+
+#include <gtest/gtest.h>
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/// \brief Run All Tests
+///
+/// A wrapper for the Google Test RUN_ALL_TESTS() macro, this calls the macro
+/// but wraps the call in a try...catch block if the environment variable
+/// B10TEST_CATCH_EXCEPTION is defined, and calls the macro directly if not.
+///
+/// The catch block catches exceptions of types isc::Exception and
+/// std::exception and prints some information about them to stderr. (In the
+/// case of isc::Exception, this includes the file and line number from which
+/// the exception was raised.) It then re-throws the exception.
+///
+/// See: https://lists.isc.org/pipermail/bind10-dev/2011-January/001867.html
+/// for some context.
+///
+/// \return Return value from RUN_ALL_TESTS().
+
+int run_all();
+
+} // namespace unittests
+} // namespace util
+} // namespace isc
+
+
+
+#endif // __RUN_ALL_H
diff --git a/tests/tools/badpacket/Makefile.am b/tests/tools/badpacket/Makefile.am
index 7df7077..fcba404 100644
--- a/tests/tools/badpacket/Makefile.am
+++ b/tests/tools/badpacket/Makefile.am
@@ -29,5 +29,5 @@ badpacket_LDADD = $(top_builddir)/src/lib/asiodns/libasiodns.la
badpacket_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
badpacket_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
badpacket_LDADD += $(top_builddir)/src/lib/log/liblog.la
+badpacket_LDADD += $(top_builddir)/src/lib/util/libutil.la
badpacket_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-
diff --git a/tests/tools/badpacket/tests/Makefile.am b/tests/tools/badpacket/tests/Makefile.am
index e83c3b6..2daa664 100644
--- a/tests/tools/badpacket/tests/Makefile.am
+++ b/tests/tools/badpacket/tests/Makefile.am
@@ -21,12 +21,12 @@ run_unittests_SOURCES += $(top_builddir)/tests/tools/badpacket/command_options.c
run_unittests_SOURCES += $(top_builddir)/tests/tools/badpacket/option_info.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDFLAGS += $(top_builddir)/src/lib/log/liblog.la
-run_unittests_LDFLAGS += $(top_builddir)/src/lib/exceptions/libexceptions.la
-run_unittests_LDFLAGS += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/tests/tools/badpacket/tests/run_unittests.cc b/tests/tools/badpacket/tests/run_unittests.cc
index 624cf6f..6eeca75 100644
--- a/tests/tools/badpacket/tests/run_unittests.cc
+++ b/tests/tools/badpacket/tests/run_unittests.cc
@@ -15,10 +15,11 @@
#include <config.h>
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
More information about the bind10-changes
mailing list