Parent Directory
|
Revision Log
Revision 38 - (view) (download) (as text)
| 1 : | mark | 38 | /* |
| 2 : | Copyright (C) 2008 - Mark Burgess | ||
| 3 : | |||
| 4 : | This file is part of Cfengine 3 - written and maintained by Mark Burgess. | ||
| 5 : | |||
| 6 : | This program is free software; you can redistribute it and/or modify it | ||
| 7 : | under the terms of the GNU General Public License as published by the | ||
| 8 : | Free Software Foundation; either version 3, or (at your option) any | ||
| 9 : | later version. | ||
| 10 : | This program is distributed in the hope that it will be useful, | ||
| 11 : | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 : | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 : | GNU General Public License for more details. | ||
| 14 : | |||
| 15 : | You should have received a copy of the GNU General Public License | ||
| 16 : | |||
| 17 : | along with this program; if not, write to the Free Software | ||
| 18 : | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
| 19 : | |||
| 20 : | */ | ||
| 21 : | |||
| 22 : | /*****************************************************************************/ | ||
| 23 : | /* */ | ||
| 24 : | /* File: files_transform.c */ | ||
| 25 : | /* */ | ||
| 26 : | /*****************************************************************************/ | ||
| 27 : | |||
| 28 : | #include "cf3.defs.h" | ||
| 29 : | #include "cf3.extern.h" | ||
| 30 : | |||
| 31 : | /*******************************************************************/ | ||
| 32 : | |||
| 33 : | struct FileAttr GetFileAttributes(struct Promise *pp) | ||
| 34 : | |||
| 35 : | { struct FileAttr attr; | ||
| 36 : | |||
| 37 : | attr.havedepthsearch = GetBooleanConstraint("depth_search",pp->conlist); | ||
| 38 : | attr.haveselect = GetBooleanConstraint("file_select",pp->conlist); | ||
| 39 : | attr.haverename = GetBooleanConstraint("rename",pp->conlist); | ||
| 40 : | attr.havedelete = GetBooleanConstraint("delete",pp->conlist); | ||
| 41 : | attr.haveperms = GetBooleanConstraint("perms",pp->conlist); | ||
| 42 : | attr.havechange = GetBooleanConstraint("changes",pp->conlist); | ||
| 43 : | attr.havecopy = GetBooleanConstraint("copy_from",pp->conlist); | ||
| 44 : | attr.havelink = GetBooleanConstraint("link_from",pp->conlist); | ||
| 45 : | attr.haveeditline = GetBooleanConstraint("edit_line",pp->conlist); | ||
| 46 : | attr.haveeditxml = GetBooleanConstraint("edit_xml",pp->conlist); | ||
| 47 : | attr.haveedit = attr.haveeditline || attr.haveeditxml; | ||
| 48 : | |||
| 49 : | /* Files, specialist - see files_transform.c */ | ||
| 50 : | |||
| 51 : | attr.repository = (char *)GetConstraint("repository",pp->conlist,CF_SCALAR); | ||
| 52 : | attr.create = GetBooleanConstraint("create",pp->conlist); | ||
| 53 : | attr.touch = GetBooleanConstraint("touch",pp->conlist); | ||
| 54 : | attr.transformer = (char *)GetConstraint("transformer",pp->conlist,CF_SCALAR); | ||
| 55 : | attr.move_obstructions = GetBooleanConstraint("move_obstruction",pp->conlist); | ||
| 56 : | |||
| 57 : | attr.perms = GetPermissionConstraints(pp); | ||
| 58 : | attr.select = GetSelectConstraints(pp); | ||
| 59 : | attr.delete = GetDeleteConstraints(pp); | ||
| 60 : | attr.rename = GetRenameConstraints(pp); | ||
| 61 : | attr.change = GetChangeMgtConstraints(pp); | ||
| 62 : | attr.copy = GetCopyConstraints(pp); | ||
| 63 : | attr.link = GetLinkConstraints(pp); | ||
| 64 : | |||
| 65 : | /* Files, multiple use */ | ||
| 66 : | |||
| 67 : | attr.recursion = GetRecursionConstraints(pp); | ||
| 68 : | |||
| 69 : | /* Common ("included") */ | ||
| 70 : | |||
| 71 : | attr.transaction = GetTransactionConstraints(pp); | ||
| 72 : | attr.classes = GetClassDefinitionConstraints(pp); | ||
| 73 : | |||
| 74 : | if (DEBUG) | ||
| 75 : | { | ||
| 76 : | ShowAttributes(attr); | ||
| 77 : | } | ||
| 78 : | |||
| 79 : | return attr; | ||
| 80 : | } | ||
| 81 : | |||
| 82 : | /*******************************************************************/ | ||
| 83 : | /* Level */ | ||
| 84 : | /*******************************************************************/ | ||
| 85 : | |||
| 86 : | struct Recursion GetRecursionConstraints(struct Promise *pp) | ||
| 87 : | |||
| 88 : | { struct Recursion r; | ||
| 89 : | r.travlinks = GetBooleanConstraint("traverse_links",pp->conlist); | ||
| 90 : | r.rmdeadlinks = GetBooleanConstraint("rmdeadlinks",pp->conlist); | ||
| 91 : | r.depth = GetIntConstraint("depth",pp->conlist); | ||
| 92 : | r.xdev = GetBooleanConstraint("xdev",pp->conlist); | ||
| 93 : | r.include_dirs = GetListConstraint("include_dirs",pp->conlist); | ||
| 94 : | r.exclude_dirs = GetListConstraint("exclude_dirs",pp->conlist); | ||
| 95 : | r.include_basedir = GetBooleanConstraint("include_basedir",pp->conlist); | ||
| 96 : | return r; | ||
| 97 : | } | ||
| 98 : | |||
| 99 : | /*******************************************************************/ | ||
| 100 : | |||
| 101 : | struct FilePerms GetPermissionConstraints(struct Promise *pp) | ||
| 102 : | |||
| 103 : | { struct FilePerms p; | ||
| 104 : | char *value; | ||
| 105 : | |||
| 106 : | value = (char *)GetConstraint("mode",pp->conlist,CF_SCALAR); | ||
| 107 : | ParseModeString(value,&p.plus,&p.minus); | ||
| 108 : | value = (char *)GetConstraint("bsdflags",pp->conlist,CF_SCALAR); | ||
| 109 : | ParseFlagString(value,&p.plus_flags,&p.minus_flags); | ||
| 110 : | p.owners = Rlist2UidList((struct Rlist *)GetConstraint("owner",pp->conlist,CF_LIST),pp); | ||
| 111 : | p.groups = Rlist2GidList((struct Rlist *)GetConstraint("group",pp->conlist,CF_LIST),pp); | ||
| 112 : | p.findertype = (char *)GetConstraint("findertype",pp->conlist,CF_SCALAR); | ||
| 113 : | p.rxdirs = GetBooleanConstraint("rxdirs",pp->conlist); | ||
| 114 : | |||
| 115 : | return p; | ||
| 116 : | } | ||
| 117 : | |||
| 118 : | /*******************************************************************/ | ||
| 119 : | |||
| 120 : | struct FileSelect GetSelectConstraints(struct Promise *pp) | ||
| 121 : | |||
| 122 : | { struct FileSelect s; | ||
| 123 : | char *value; | ||
| 124 : | |||
| 125 : | s.name = (struct Rlist *)GetConstraint("leaf_name",pp->conlist,CF_LIST); | ||
| 126 : | s.path = (struct Rlist *)GetConstraint("path_name",pp->conlist,CF_LIST); | ||
| 127 : | s.filetypes = (struct Rlist *)GetConstraint("file_types",pp->conlist,CF_LIST); | ||
| 128 : | s.issymlinkto = (struct Rlist *)GetConstraint("issymlinkto",pp->conlist,CF_LIST); | ||
| 129 : | |||
| 130 : | value = (char *)GetConstraint("search_mode",pp->conlist,CF_SCALAR); | ||
| 131 : | ParseModeString(value,&s.plus,&s.minus); | ||
| 132 : | value = (char *)GetConstraint("search_bsdflags",pp->conlist,CF_SCALAR); | ||
| 133 : | ParseFlagString(value,&s.plus_flags,&s.minus_flags); | ||
| 134 : | |||
| 135 : | s.owners = (struct Rlist *)GetConstraint("search_owner",pp->conlist,CF_LIST); | ||
| 136 : | s.groups = (struct Rlist *)GetConstraint("search_group",pp->conlist,CF_LIST); | ||
| 137 : | |||
| 138 : | value = (char *)GetConstraint("search_size",pp->conlist,CF_SCALAR); | ||
| 139 : | IntRange2Int(value,&s.min_size,&s.max_size,pp); | ||
| 140 : | value = (char *)GetConstraint("ctime",pp->conlist,CF_SCALAR); | ||
| 141 : | IntRange2Int(value,&s.min_ctime,&s.max_ctime,pp); | ||
| 142 : | value = (char *)GetConstraint("atime",pp->conlist,CF_SCALAR); | ||
| 143 : | IntRange2Int(value,&s.min_atime,&s.max_atime,pp); | ||
| 144 : | value = (char *)GetConstraint("mtime",pp->conlist,CF_SCALAR); | ||
| 145 : | IntRange2Int(value,&s.min_mtime,&s.max_mtime,pp); | ||
| 146 : | s.exec_regex = (char *)GetConstraint("exec_regex",pp->conlist,CF_SCALAR); | ||
| 147 : | s.exec_program = (char *)GetConstraint("exec_program",pp->conlist,CF_SCALAR); | ||
| 148 : | |||
| 149 : | if ((s.result = (char *)GetConstraint("file_result",pp->conlist,CF_SCALAR)) == NULL) | ||
| 150 : | { | ||
| 151 : | s.result = "leaf_name.path_name.file_types.owner.group.mode.ctime.mtime.atime.size.exec_regex.issymlinkto.exec_program"; | ||
| 152 : | } | ||
| 153 : | |||
| 154 : | return s; | ||
| 155 : | } | ||
| 156 : | |||
| 157 : | /*******************************************************************/ | ||
| 158 : | |||
| 159 : | struct TransactionContext GetTransactionConstraints(struct Promise *pp) | ||
| 160 : | |||
| 161 : | { struct TransactionContext t; | ||
| 162 : | char *value; | ||
| 163 : | |||
| 164 : | value = GetConstraint("action",pp->conlist,CF_SCALAR); | ||
| 165 : | |||
| 166 : | if (value && strcmp(value,"warn") == 0) | ||
| 167 : | { | ||
| 168 : | t.action = cfa_warn; | ||
| 169 : | } | ||
| 170 : | else | ||
| 171 : | { | ||
| 172 : | t.action = cfa_fix; // default | ||
| 173 : | } | ||
| 174 : | |||
| 175 : | t.background = GetBooleanConstraint("background",pp->conlist); | ||
| 176 : | t.ifelapsed = GetIntConstraint("ifelapsed",pp->conlist); | ||
| 177 : | |||
| 178 : | if (t.ifelapsed == CF_UNDEFINED) | ||
| 179 : | { | ||
| 180 : | t.ifelapsed = VIFELAPSED; | ||
| 181 : | } | ||
| 182 : | |||
| 183 : | t.expireafter = GetIntConstraint("expireafter",pp->conlist); | ||
| 184 : | |||
| 185 : | if (t.expireafter == CF_UNDEFINED) | ||
| 186 : | { | ||
| 187 : | t.expireafter = VEXPIREAFTER; | ||
| 188 : | } | ||
| 189 : | |||
| 190 : | t.log_string = GetConstraint("log_string",pp->conlist,CF_SCALAR); | ||
| 191 : | t.log_level = GetConstraint("log_level",pp->conlist,CF_SCALAR); | ||
| 192 : | t.audit = GetBooleanConstraint("audit",pp->conlist); | ||
| 193 : | |||
| 194 : | value = GetConstraint("report_level",pp->conlist,CF_SCALAR); | ||
| 195 : | |||
| 196 : | if (value && strcmp(value,"verbose") == 0) | ||
| 197 : | { | ||
| 198 : | t.report_level = cfverbose; | ||
| 199 : | } | ||
| 200 : | else if (value && strcmp(value,"error") == 0) | ||
| 201 : | { | ||
| 202 : | t.report_level = cferror; | ||
| 203 : | } | ||
| 204 : | else if (value && strcmp(value,"logonly") == 0) | ||
| 205 : | { | ||
| 206 : | t.report_level = cflogonly; | ||
| 207 : | } | ||
| 208 : | else if (value && strcmp(value,"loginform") == 0) | ||
| 209 : | { | ||
| 210 : | t.report_level = cfloginform; | ||
| 211 : | } | ||
| 212 : | else | ||
| 213 : | { | ||
| 214 : | t.report_level = cfsilent; | ||
| 215 : | } | ||
| 216 : | |||
| 217 : | return t; | ||
| 218 : | } | ||
| 219 : | |||
| 220 : | /*******************************************************************/ | ||
| 221 : | |||
| 222 : | struct DefineClasses GetClassDefinitionConstraints(struct Promise *pp) | ||
| 223 : | |||
| 224 : | { struct DefineClasses c; | ||
| 225 : | |||
| 226 : | c.change = (struct Rlist *)GetConstraint("change",pp->conlist,CF_LIST); | ||
| 227 : | c.failure = (struct Rlist *)GetConstraint("failure",pp->conlist,CF_LIST); | ||
| 228 : | c.denied = (struct Rlist *)GetConstraint("denied",pp->conlist,CF_LIST); | ||
| 229 : | c.timeout = (struct Rlist *)GetConstraint("timeout",pp->conlist,CF_LIST); | ||
| 230 : | c.interrupt = (struct Rlist *)GetConstraint("interrupt",pp->conlist,CF_LIST); | ||
| 231 : | |||
| 232 : | return c; | ||
| 233 : | } | ||
| 234 : | |||
| 235 : | /*******************************************************************/ | ||
| 236 : | |||
| 237 : | struct FileDelete GetDeleteConstraints(struct Promise *pp) | ||
| 238 : | |||
| 239 : | { struct FileDelete f; | ||
| 240 : | char *value; | ||
| 241 : | |||
| 242 : | value = (char *)GetConstraint("dirlinks",pp->conlist,CF_SCALAR); | ||
| 243 : | |||
| 244 : | if (value && strcmp(value,"keep") == 0) | ||
| 245 : | { | ||
| 246 : | f.dirlinks = cfa_linkkeep; | ||
| 247 : | } | ||
| 248 : | else | ||
| 249 : | { | ||
| 250 : | f.dirlinks = cfa_linkdelete; | ||
| 251 : | } | ||
| 252 : | |||
| 253 : | f.rmdirs = GetBooleanConstraint("rmdirs",pp->conlist); | ||
| 254 : | return f; | ||
| 255 : | } | ||
| 256 : | |||
| 257 : | /*******************************************************************/ | ||
| 258 : | |||
| 259 : | struct FileRename GetRenameConstraints(struct Promise *pp) | ||
| 260 : | |||
| 261 : | { struct FileRename r; | ||
| 262 : | char *value; | ||
| 263 : | |||
| 264 : | value = (char *)GetConstraint("mode",pp->conlist,CF_SCALAR); | ||
| 265 : | ParseModeString(value,&r.plus,&r.minus); | ||
| 266 : | |||
| 267 : | r.disable = GetBooleanConstraint("disable",pp->conlist); | ||
| 268 : | r.disable_suffix = (char *)GetConstraint("disable_suffix",pp->conlist,CF_SCALAR); | ||
| 269 : | r.newname = (char *)GetConstraint("newname",pp->conlist,CF_SCALAR); | ||
| 270 : | r.rotate = GetIntConstraint("rotate",pp->conlist); | ||
| 271 : | |||
| 272 : | return r; | ||
| 273 : | } | ||
| 274 : | |||
| 275 : | /*******************************************************************/ | ||
| 276 : | |||
| 277 : | struct FileChange GetChangeMgtConstraints(struct Promise *pp) | ||
| 278 : | |||
| 279 : | { struct FileChange c; | ||
| 280 : | char *value; | ||
| 281 : | |||
| 282 : | value = (char *)GetConstraint("hash",pp->conlist,CF_SCALAR); | ||
| 283 : | |||
| 284 : | if (value && strcmp(value,"best") == 0) | ||
| 285 : | { | ||
| 286 : | c.hash = cf_besthash; | ||
| 287 : | } | ||
| 288 : | else if (value && strcmp(value,"sha1") == 0) | ||
| 289 : | { | ||
| 290 : | c.hash = cf_sha1; | ||
| 291 : | } | ||
| 292 : | else | ||
| 293 : | { | ||
| 294 : | c.hash = cf_md5; | ||
| 295 : | } | ||
| 296 : | |||
| 297 : | value = (char *)GetConstraint("reportt_changes",pp->conlist,CF_SCALAR); | ||
| 298 : | |||
| 299 : | if (value && strcmp(value,"content") == 0) | ||
| 300 : | { | ||
| 301 : | c.report_changes = cfa_contentchange; | ||
| 302 : | } | ||
| 303 : | else | ||
| 304 : | { | ||
| 305 : | c.report_changes = cfa_noreport; | ||
| 306 : | } | ||
| 307 : | |||
| 308 : | c.update = GetBooleanConstraint("update",pp->conlist); | ||
| 309 : | |||
| 310 : | return c; | ||
| 311 : | } | ||
| 312 : | |||
| 313 : | /*******************************************************************/ | ||
| 314 : | |||
| 315 : | struct FileCopy GetCopyConstraints(struct Promise *pp) | ||
| 316 : | |||
| 317 : | { struct FileCopy f; | ||
| 318 : | char *value; | ||
| 319 : | |||
| 320 : | f.source = (char *)GetConstraint("source",pp->conlist,CF_SCALAR); | ||
| 321 : | value = (char *)GetConstraint("compare",pp->conlist,CF_SCALAR); | ||
| 322 : | f.compare = String2Comparison(value); | ||
| 323 : | value = (char *)GetConstraint("link_type",pp->conlist,CF_SCALAR); | ||
| 324 : | f.link_type = String2LinkType(value); | ||
| 325 : | f.servers = GetListConstraint("servers",pp->conlist); | ||
| 326 : | f.portnumber = (short)GetIntConstraint("portnumber",pp->conlist); | ||
| 327 : | f.link_instead = GetListConstraint("linkcopy_patterns",pp->conlist); | ||
| 328 : | f.copy_links = GetListConstraint("copylink_patterns",pp->conlist); | ||
| 329 : | |||
| 330 : | value = (char *)GetConstraint("backup",pp->conlist,CF_SCALAR); | ||
| 331 : | |||
| 332 : | if (value && strcmp(value,"false") == 0) | ||
| 333 : | { | ||
| 334 : | f.backup = cfa_nobackup; | ||
| 335 : | } | ||
| 336 : | else if (value && strcmp(value,"timestamp") == 0) | ||
| 337 : | { | ||
| 338 : | f.backup = cfa_timestamp; | ||
| 339 : | } | ||
| 340 : | else | ||
| 341 : | { | ||
| 342 : | f.backup = cfa_backup; | ||
| 343 : | } | ||
| 344 : | |||
| 345 : | f.stealth = GetBooleanConstraint("stealth",pp->conlist); | ||
| 346 : | f.preserve = GetBooleanConstraint("preserve",pp->conlist); | ||
| 347 : | f.type_check = GetBooleanConstraint("type_check",pp->conlist); | ||
| 348 : | f.force_update = GetBooleanConstraint("force_update",pp->conlist); | ||
| 349 : | f.force_ipv4 = GetBooleanConstraint("force_ipv4",pp->conlist); | ||
| 350 : | f.check_root = GetBooleanConstraint("check_root",pp->conlist); | ||
| 351 : | |||
| 352 : | value = (char *)GetConstraint("copy_size",pp->conlist,CF_SCALAR); | ||
| 353 : | IntRange2Int(value,&f.min_size,&f.max_size,pp); | ||
| 354 : | |||
| 355 : | f.trustkey = GetBooleanConstraint("trustkey",pp->conlist); | ||
| 356 : | f.encrypt = GetBooleanConstraint("encrypt",pp->conlist); | ||
| 357 : | f.verify = GetBooleanConstraint("verify",pp->conlist); | ||
| 358 : | f.purge = GetBooleanConstraint("purge",pp->conlist); | ||
| 359 : | |||
| 360 : | return f; | ||
| 361 : | } | ||
| 362 : | |||
| 363 : | /*******************************************************************/ | ||
| 364 : | |||
| 365 : | struct FileLink GetLinkConstraints(struct Promise *pp) | ||
| 366 : | |||
| 367 : | { struct FileLink f; | ||
| 368 : | char *value; | ||
| 369 : | |||
| 370 : | f.source = (char *)GetConstraint("source",pp->conlist,CF_SCALAR); | ||
| 371 : | value = (char *)GetConstraint("link_type",pp->conlist,CF_SCALAR); | ||
| 372 : | f.link_type = String2LinkType(value); | ||
| 373 : | f.copy_patterns = GetConstraint("copy_patterns",pp->conlist,CF_LIST); | ||
| 374 : | |||
| 375 : | value = (char *)GetConstraint("when_no_file",pp->conlist,CF_SCALAR); | ||
| 376 : | |||
| 377 : | if (value && strcmp(value,"force") == 0) | ||
| 378 : | { | ||
| 379 : | f.when_no_file = cfa_force; | ||
| 380 : | } | ||
| 381 : | else if (value && strcmp(value,"delete") == 0) | ||
| 382 : | { | ||
| 383 : | f.when_no_file = cfa_delete; | ||
| 384 : | } | ||
| 385 : | else | ||
| 386 : | { | ||
| 387 : | f.when_no_file = cfa_skip; | ||
| 388 : | } | ||
| 389 : | |||
| 390 : | value = (char *)GetConstraint("link_children",pp->conlist,CF_SCALAR); | ||
| 391 : | |||
| 392 : | if (value && strcmp(value,"override_existing") == 0) | ||
| 393 : | { | ||
| 394 : | f.link_children = cfa_override; | ||
| 395 : | } | ||
| 396 : | else | ||
| 397 : | { | ||
| 398 : | f.link_children = cfa_onlynonexisting; | ||
| 399 : | } | ||
| 400 : | |||
| 401 : | return f; | ||
| 402 : | } | ||
| 403 : | |||
| 404 : | /*******************************************************************/ | ||
| 405 : | |||
| 406 : | void ShowAttributes(struct FileAttr a) | ||
| 407 : | |||
| 408 : | { | ||
| 409 : | printf(".....................................................\n"); | ||
| 410 : | printf("File Attribute Set =\n\n"); | ||
| 411 : | |||
| 412 : | if (a.havedepthsearch) printf(" * havedepthsearch\n"); | ||
| 413 : | if (a.haveselect) printf(" * haveselect\n"); | ||
| 414 : | if (a.haverename) printf(" * haverename\n"); | ||
| 415 : | if (a.havedelete) printf(" * havedelete\n"); | ||
| 416 : | if (a.haveperms) printf(" * haveperms\n"); | ||
| 417 : | if (a.havechange) printf(" * havechange\n"); | ||
| 418 : | if (a.havecopy) printf(" * havecopy\n"); | ||
| 419 : | if (a.havelink) printf(" * havelink\n"); | ||
| 420 : | if (a.haveedit) printf(" * haveedit\n"); | ||
| 421 : | if (a.create) printf(" * havecreate\n"); | ||
| 422 : | if (a.touch) printf(" * havetouch\n"); | ||
| 423 : | if (a.move_obstructions) printf(" * move_obstructions\n"); | ||
| 424 : | |||
| 425 : | if (a.repository) printf(" * repository %s\n",a.repository); | ||
| 426 : | if (a.transformer)printf(" * transformer %s\n",a.transformer); | ||
| 427 : | |||
| 428 : | /* | ||
| 429 : | if (a.perms) printf(" * perms %o\n",a.perms.mode); | ||
| 430 : | a.select = GetSelectConstraints(pp); | ||
| 431 : | a.delete = GetDeleteConstraints(pp); | ||
| 432 : | a.rename = GetRenameConstraints(pp); | ||
| 433 : | a.change = GetChangeMgtConstraints(pp); | ||
| 434 : | a.copy = GetCopyConstraints(pp); | ||
| 435 : | a.link = GetLinkConstraints(pp); | ||
| 436 : | a.recursion = GetRecursionConstraints(pp); | ||
| 437 : | |||
| 438 : | a.transaction = GetTransactionConstraints(pp); | ||
| 439 : | a.classes = GetClassDefinitionConstraints(pp); | ||
| 440 : | */ | ||
| 441 : | printf(".....................................................\n\n"); | ||
| 442 : | } | ||
| 443 : |
| Administrator | ViewVC Help |
| Powered by ViewVC 1.0.3 |