1 /* $OpenBSD: nfs.c,v 1.10 2024/05/18 09:02:34 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Jasper Lievisse Adriaanse <jasper@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 */ 19 20 #include <sys/types.h> 21 #include <sys/mount.h> 22 #include <sys/signal.h> 23 #include <sys/sysctl.h> 24 #include <nfs/rpcv2.h> 25 #include <nfs/nfsproto.h> 26 #include <nfs/nfs.h> 27 28 #include <err.h> 29 #include <errno.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 34 #include "systat.h" 35 36 int select_client(void); 37 int select_server(void); 38 int read_nfs(void); 39 void print_client(void); 40 void print_server(void); 41 42 struct nfsstats nfsstats; 43 int num_client = 0; 44 int num_server = 0; 45 46 field_def fields_nfs[] = { 47 /* Client */ 48 {"RPC COUNTS", 10, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, 49 {"", 12, 14, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, 50 {"RPC INFO", 14, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, 51 {"", 12, 14, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, 52 {"CACHE INFO", 10, 12, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, 53 {"", 12, 14, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, 54 55 /* Server */ 56 {"RPC COUNTS", 10, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, 57 {"", 12, 14, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, 58 {"CACHE STATS", 14, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, 59 {"", 12, 14, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, 60 {"WRITES", 10, 12, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, 61 {"", 10, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, 62 }; 63 64 /* _V suffixed fields indicate a value column. */ 65 /* Client */ 66 #define FLD_NFS_C_RPC_COUNTS FIELD_ADDR(fields_nfs,0) 67 #define FLD_NFS_C_RPC_COUNTS_V FIELD_ADDR(fields_nfs,1) 68 #define FLD_NFS_C_RPC_INFO FIELD_ADDR(fields_nfs,2) 69 #define FLD_NFS_C_RPC_INFO_V FIELD_ADDR(fields_nfs,3) 70 #define FLD_NFS_C_CACHE_INFO FIELD_ADDR(fields_nfs,4) 71 #define FLD_NFS_C_CACHE_V FIELD_ADDR(fields_nfs,5) 72 73 /* Server */ 74 #define FLD_NFS_S_RPC_COUNTS FIELD_ADDR(fields_nfs,6) 75 #define FLD_NFS_S_RPC_COUNTS_V FIELD_ADDR(fields_nfs,7) 76 #define FLD_NFS_S_CACHE_STATS FIELD_ADDR(fields_nfs,8) 77 #define FLD_NFS_S_CACHE_STATS_V FIELD_ADDR(fields_nfs,9) 78 #define FLD_NFS_S_WRITES FIELD_ADDR(fields_nfs,10) 79 #define FLD_NFS_S_WRITES_V FIELD_ADDR(fields_nfs,11) 80 81 /* Define views */ 82 field_def *view_nfs_0[] = { 83 FLD_NFS_C_RPC_COUNTS, FLD_NFS_C_RPC_COUNTS_V, FLD_NFS_C_RPC_INFO, 84 FLD_NFS_C_RPC_INFO_V, FLD_NFS_C_CACHE_INFO, FLD_NFS_C_CACHE_V ,NULL 85 }; 86 87 field_def *view_nfs_1[] = { 88 FLD_NFS_S_RPC_COUNTS, FLD_NFS_S_RPC_COUNTS_V, FLD_NFS_S_CACHE_STATS, 89 FLD_NFS_S_CACHE_STATS_V, FLD_NFS_S_WRITES, FLD_NFS_S_WRITES_V, NULL 90 }; 91 92 /* Define view managers */ 93 struct view_manager nfs_client_mgr = { 94 "Client", select_client, read_nfs, NULL, print_header, 95 print_client, keyboard_callback, NULL, NULL 96 }; 97 98 struct view_manager nfs_server_mgr = { 99 "Server", select_server, read_nfs, NULL, print_header, 100 print_server, keyboard_callback, NULL, NULL 101 }; 102 103 field_view views_nfs[] = { 104 {view_nfs_0, "nfsclient", '8', &nfs_client_mgr}, 105 {view_nfs_1, "nfsserver", '9', &nfs_server_mgr}, 106 {NULL, NULL, 0, NULL} 107 }; 108 109 int 110 select_client(void) 111 { 112 num_disp = num_client; 113 return(0); 114 } 115 116 int 117 select_server(void) 118 { 119 num_disp = num_server; 120 return(0); 121 } 122 123 int 124 initnfs(void) 125 { 126 field_view *v; 127 128 for (v = views_nfs; v->name != NULL; v++) 129 add_view(v); 130 131 read_nfs(); 132 133 return(0); 134 } 135 136 /* 137 * We get all the information in one go and don't care about 138 * server or client fields (those will be '0' if not applicable). 139 */ 140 int 141 read_nfs(void) 142 { 143 struct nfsstats *p = &nfsstats; 144 int mib[3]; 145 size_t len = sizeof(*p); 146 147 mib[0] = CTL_VFS; 148 mib[1] = 2; /* NETDEV */ 149 mib[2] = NFS_NFSSTATS; 150 151 if (sysctl(mib, 3, p, &len, NULL, 0) == -1) 152 return(-1); 153 else 154 return(0); 155 } 156 157 158 /* 159 * As we want a view with multiple columns, mixed with labels and values, 160 * we can't use the regular dance and have to use our own (looong) dance 161 * to build the layout. 162 */ 163 void 164 print_client(void) 165 { 166 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Getattr"); 167 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 168 nfsstats.rpccnt[NFSPROC_GETATTR]); 169 print_fld_str(FLD_NFS_C_RPC_INFO, "TimedOut"); 170 print_fld_ssize(FLD_NFS_C_RPC_INFO_V, nfsstats.rpctimeouts); 171 print_fld_str(FLD_NFS_C_CACHE_INFO, "Attr Hits "); 172 print_fld_ssize(FLD_NFS_C_CACHE_V, nfsstats.attrcache_hits); 173 end_line(); 174 175 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Setattr"); 176 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 177 nfsstats.rpccnt[NFSPROC_SETATTR]); 178 print_fld_str(FLD_NFS_C_RPC_INFO, "Invalid"); 179 print_fld_ssize(FLD_NFS_C_RPC_INFO_V, nfsstats.rpcinvalid); 180 print_fld_str(FLD_NFS_C_CACHE_INFO, "Attr Misses"); 181 print_fld_ssize(FLD_NFS_C_CACHE_V, nfsstats.attrcache_misses); 182 end_line(); 183 184 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Lookup"); 185 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 186 nfsstats.rpccnt[NFSPROC_LOOKUP]); 187 print_fld_str(FLD_NFS_C_RPC_INFO, "X Replies"); 188 print_fld_ssize(FLD_NFS_C_RPC_INFO_V, nfsstats.rpcunexpected); 189 print_fld_str(FLD_NFS_C_CACHE_INFO, "Lkup Hits "); 190 print_fld_ssize(FLD_NFS_C_CACHE_V, nfsstats.lookupcache_hits); 191 end_line(); 192 193 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Readlink"); 194 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 195 nfsstats.rpccnt[NFSPROC_READLINK]); 196 print_fld_str(FLD_NFS_C_RPC_INFO, "Retries"); 197 print_fld_ssize(FLD_NFS_C_RPC_INFO_V, nfsstats.rpcretries); 198 print_fld_str(FLD_NFS_C_CACHE_INFO, "Lkup Misses "); 199 print_fld_ssize(FLD_NFS_C_CACHE_V, nfsstats.lookupcache_misses); 200 end_line(); 201 202 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Read"); 203 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 204 nfsstats.rpccnt[NFSPROC_READ]); 205 print_fld_str(FLD_NFS_C_RPC_INFO, "Requests"); 206 print_fld_ssize(FLD_NFS_C_RPC_INFO_V, nfsstats.rpcrequests); 207 print_fld_str(FLD_NFS_C_CACHE_INFO, "BioR Hits "); 208 print_fld_ssize(FLD_NFS_C_CACHE_V, 209 nfsstats.biocache_reads-nfsstats.read_bios); 210 end_line(); 211 212 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Write"); 213 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, nfsstats.rpccnt[NFSPROC_WRITE]); 214 print_fld_str(FLD_NFS_C_RPC_INFO, "FrcSync"); 215 print_fld_ssize(FLD_NFS_C_RPC_INFO_V, nfsstats.forcedsync); 216 print_fld_str(FLD_NFS_C_CACHE_INFO, "BioR Misses"); 217 print_fld_ssize(FLD_NFS_C_CACHE_V, nfsstats.read_bios); 218 end_line(); 219 220 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Create"); 221 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 222 nfsstats.rpccnt[NFSPROC_CREATE]); 223 print_fld_str(FLD_NFS_C_CACHE_INFO, "BioW Hits "); 224 print_fld_ssize(FLD_NFS_C_CACHE_V, 225 nfsstats.biocache_writes-nfsstats.write_bios); 226 end_line(); 227 228 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Remove"); 229 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 230 nfsstats.rpccnt[NFSPROC_REMOVE]); 231 print_fld_str(FLD_NFS_C_CACHE_INFO, "BioW Misses"); 232 print_fld_ssize(FLD_NFS_C_CACHE_V, nfsstats.write_bios); 233 end_line(); 234 235 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Rename"); 236 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 237 nfsstats.rpccnt[NFSPROC_RENAME]); 238 print_fld_str(FLD_NFS_C_CACHE_INFO, "BioRL Hits "); 239 print_fld_ssize(FLD_NFS_C_CACHE_V, 240 nfsstats.biocache_readlinks-nfsstats.readlink_bios); 241 end_line(); 242 243 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Link"); 244 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, nfsstats.rpccnt[NFSPROC_LINK]); 245 print_fld_str(FLD_NFS_C_CACHE_INFO, "BioRL Misses"); 246 print_fld_ssize(FLD_NFS_C_CACHE_V, nfsstats.readlink_bios); 247 end_line(); 248 249 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Symlink"); 250 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 251 nfsstats.rpccnt[NFSPROC_SYMLINK]); 252 print_fld_str(FLD_NFS_C_CACHE_INFO, "BioD Hits "); 253 print_fld_ssize(FLD_NFS_C_CACHE_V, 254 nfsstats.biocache_readdirs-nfsstats.readdir_bios); 255 end_line(); 256 257 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Mkdir"); 258 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, nfsstats.rpccnt[NFSPROC_MKDIR]); 259 print_fld_str(FLD_NFS_C_CACHE_INFO, "BioD Misses"); 260 print_fld_ssize(FLD_NFS_C_CACHE_V, nfsstats.readdir_bios); 261 end_line(); 262 263 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Rmdir"); 264 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, nfsstats.rpccnt[NFSPROC_RMDIR]); 265 print_fld_str(FLD_NFS_C_CACHE_INFO, "DirE Hits "); 266 print_fld_ssize(FLD_NFS_C_CACHE_V, nfsstats.direofcache_hits); 267 end_line(); 268 269 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Readdir"); 270 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 271 nfsstats.rpccnt[NFSPROC_READDIR]); 272 print_fld_str(FLD_NFS_C_CACHE_INFO, "DirE Misses"); 273 print_fld_ssize(FLD_NFS_C_CACHE_V, nfsstats.direofcache_misses); 274 end_line(); 275 276 print_fld_str(FLD_NFS_C_RPC_COUNTS, "RdirPlus"); 277 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 278 nfsstats.rpccnt[NFSPROC_READDIRPLUS]); 279 end_line(); 280 281 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Access"); 282 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 283 nfsstats.rpccnt[NFSPROC_ACCESS]); 284 end_line(); 285 286 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Mknod"); 287 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, nfsstats.rpccnt[NFSPROC_MKNOD]); 288 end_line(); 289 290 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Fsstat"); 291 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 292 nfsstats.rpccnt[NFSPROC_FSSTAT]); 293 end_line(); 294 295 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Fsinfo"); 296 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 297 nfsstats.rpccnt[NFSPROC_FSINFO]); 298 end_line(); 299 300 print_fld_str(FLD_NFS_C_RPC_COUNTS, "PathConf"); 301 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 302 nfsstats.rpccnt[NFSPROC_PATHCONF]); 303 end_line(); 304 305 print_fld_str(FLD_NFS_C_RPC_COUNTS, "Commit"); 306 print_fld_ssize(FLD_NFS_C_RPC_COUNTS_V, 307 nfsstats.rpccnt[NFSPROC_COMMIT]); 308 end_line(); 309 } 310 311 void 312 print_server(void) 313 { 314 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Getattr"); 315 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 316 nfsstats.srvrpccnt[NFSPROC_GETATTR]); 317 print_fld_str(FLD_NFS_S_CACHE_STATS, "Inprog"); 318 print_fld_ssize(FLD_NFS_S_CACHE_STATS_V, nfsstats.srvcache_inproghits); 319 print_fld_str(FLD_NFS_S_WRITES, "WriteOps"); 320 print_fld_ssize(FLD_NFS_S_WRITES_V, nfsstats.srvvop_writes); 321 end_line(); 322 323 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Setattr"); 324 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 325 nfsstats.srvrpccnt[NFSPROC_SETATTR]); 326 print_fld_str(FLD_NFS_S_CACHE_STATS, "Idem"); 327 print_fld_ssize(FLD_NFS_S_CACHE_STATS_V, 328 nfsstats.srvcache_idemdonehits); 329 print_fld_str(FLD_NFS_S_WRITES, "WriteRPC"); 330 print_fld_ssize(FLD_NFS_S_WRITES_V, nfsstats.srvrpccnt[NFSPROC_WRITE]); 331 end_line(); 332 333 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Lookup"); 334 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 335 nfsstats.srvrpccnt[NFSPROC_LOOKUP]); 336 print_fld_str(FLD_NFS_S_CACHE_STATS, "Non-idem"); 337 print_fld_ssize(FLD_NFS_S_CACHE_STATS_V, 338 nfsstats.srvcache_nonidemdonehits); 339 print_fld_str(FLD_NFS_S_WRITES, "Opsaved"); 340 print_fld_ssize(FLD_NFS_S_WRITES_V, 341 nfsstats.srvrpccnt[NFSPROC_WRITE] - nfsstats.srvvop_writes); 342 end_line(); 343 344 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Readlink"); 345 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 346 nfsstats.srvrpccnt[NFSPROC_READLINK]); 347 print_fld_str(FLD_NFS_S_CACHE_STATS, "Misses"); 348 print_fld_ssize(FLD_NFS_S_CACHE_STATS_V, nfsstats.srvcache_misses); 349 end_line(); 350 351 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Read"); 352 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 353 nfsstats.srvrpccnt[NFSPROC_READ]); 354 end_line(); 355 356 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Write"); 357 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 358 nfsstats.srvrpccnt[NFSPROC_WRITE]); 359 end_line(); 360 361 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Create"); 362 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 363 nfsstats.srvrpccnt[NFSPROC_CREATE]); 364 end_line(); 365 366 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Remove"); 367 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 368 nfsstats.srvrpccnt[NFSPROC_REMOVE]); 369 end_line(); 370 371 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Rename"); 372 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 373 nfsstats.srvrpccnt[NFSPROC_RENAME]); 374 end_line(); 375 376 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Link"); 377 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 378 nfsstats.srvrpccnt[NFSPROC_LINK]); 379 end_line(); 380 381 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Symlink"); 382 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 383 nfsstats.srvrpccnt[NFSPROC_SYMLINK]); 384 end_line(); 385 386 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Mkdir"); 387 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 388 nfsstats.srvrpccnt[NFSPROC_MKDIR]); 389 end_line(); 390 391 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Rmdir"); 392 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 393 nfsstats.srvrpccnt[NFSPROC_RMDIR]); 394 end_line(); 395 396 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Readdir"); 397 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 398 nfsstats.srvrpccnt[NFSPROC_READDIR]); 399 end_line(); 400 401 print_fld_str(FLD_NFS_S_RPC_COUNTS, "RdirPlus"); 402 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 403 nfsstats.srvrpccnt[NFSPROC_READDIRPLUS]); 404 end_line(); 405 406 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Access"); 407 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 408 nfsstats.srvrpccnt[NFSPROC_ACCESS]); 409 end_line(); 410 411 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Mknod"); 412 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 413 nfsstats.srvrpccnt[NFSPROC_MKNOD]); 414 end_line(); 415 416 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Fsstat"); 417 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 418 nfsstats.srvrpccnt[NFSPROC_FSSTAT]); 419 end_line(); 420 421 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Fsinfo"); 422 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 423 nfsstats.srvrpccnt[NFSPROC_FSINFO]); 424 end_line(); 425 426 print_fld_str(FLD_NFS_S_RPC_COUNTS, "PathConf"); 427 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 428 nfsstats.srvrpccnt[NFSPROC_PATHCONF]); 429 end_line(); 430 431 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Commit"); 432 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, 433 nfsstats.srvrpccnt[NFSPROC_COMMIT]); 434 end_line(); 435 436 /* This creates an empty space on screen to separate the two blocks */ 437 print_fld_str(FLD_NFS_S_RPC_COUNTS, ""); 438 end_line(); 439 440 print_fld_str(FLD_NFS_S_RPC_COUNTS, "Ret-Failed"); 441 print_fld_ssize(FLD_NFS_S_RPC_COUNTS_V, nfsstats.srvrpc_errs); 442 print_fld_str(FLD_NFS_S_CACHE_STATS, "Faults"); 443 print_fld_ssize(FLD_NFS_S_CACHE_STATS_V, nfsstats.srv_errs); 444 end_line(); 445 } 446