Changes in uspace/srv/devmap/devmap.c [4ae90f9:ab108be4] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/srv/devmap/devmap.c
r4ae90f9 rab108be4 46 46 #include <str.h> 47 47 #include <ipc/devmap.h> 48 #include <assert.h>49 48 50 49 #define NAME "devmap" … … 62 61 link_t devices; 63 62 /** Phone asociated with this driver */ 64 sysarg_t phone;63 ipcarg_t phone; 65 64 /** Device driver name */ 66 65 char *name; … … 76 75 link_t namespaces; 77 76 /** Unique namespace identifier */ 78 dev map_handle_t handle;77 dev_handle_t handle; 79 78 /** Namespace name */ 80 79 char *name; … … 93 92 link_t driver_devices; 94 93 /** Unique device identifier */ 95 dev map_handle_t handle;94 dev_handle_t handle; 96 95 /** Device namespace */ 97 96 devmap_namespace_t *namespace; … … 100 99 /** Device driver handling this device */ 101 100 devmap_driver_t *driver; 102 /** Use this interface when forwarding to driver. */103 sysarg_t forward_interface;104 101 } devmap_device_t; 105 102 … … 121 118 static FIBRIL_MUTEX_INITIALIZE(null_devices_mutex); 122 119 123 static dev map_handle_t last_handle = 0;120 static dev_handle_t last_handle = 0; 124 121 static devmap_device_t *null_devices[NULL_DEVICES]; 125 122 126 /* 127 * Dummy list for null devices. This is necessary so that null devices can 128 * be used just as any other devices, e.g. in devmap_device_unregister_core(). 129 */ 130 static LIST_INITIALIZE(dummy_null_driver_devices); 131 132 static devmap_handle_t devmap_create_handle(void) 123 static dev_handle_t devmap_create_handle(void) 133 124 { 134 125 /* TODO: allow reusing old handles after their unregistration … … 215 206 } 216 207 217 /** Find namespace with given name. */ 208 /** Find namespace with given name. 209 * 210 * The devices_list_mutex should be already held when 211 * calling this function. 212 * 213 */ 218 214 static devmap_namespace_t *devmap_namespace_find_name(const char *name) 219 215 { 220 216 link_t *item; 221 222 assert(fibril_mutex_is_locked(&devices_list_mutex));223 224 217 for (item = namespaces_list.next; item != &namespaces_list; item = item->next) { 225 218 devmap_namespace_t *namespace = … … 234 227 /** Find namespace with given handle. 235 228 * 229 * The devices_list_mutex should be already held when 230 * calling this function. 231 * 236 232 * @todo: use hash table 237 233 * 238 234 */ 239 static devmap_namespace_t *devmap_namespace_find_handle(dev map_handle_t handle)235 static devmap_namespace_t *devmap_namespace_find_handle(dev_handle_t handle) 240 236 { 241 237 link_t *item; 242 243 assert(fibril_mutex_is_locked(&devices_list_mutex));244 245 238 for (item = namespaces_list.next; item != &namespaces_list; item = item->next) { 246 239 devmap_namespace_t *namespace = … … 253 246 } 254 247 255 /** Find device with given name. */ 248 /** Find device with given name. 249 * 250 * The devices_list_mutex should be already held when 251 * calling this function. 252 * 253 */ 256 254 static devmap_device_t *devmap_device_find_name(const char *ns_name, 257 255 const char *name) 258 256 { 259 257 link_t *item; 260 261 assert(fibril_mutex_is_locked(&devices_list_mutex));262 263 258 for (item = devices_list.next; item != &devices_list; item = item->next) { 264 259 devmap_device_t *device = … … 274 269 /** Find device with given handle. 275 270 * 271 * The devices_list_mutex should be already held when 272 * calling this function. 273 * 276 274 * @todo: use hash table 277 275 * 278 276 */ 279 static devmap_device_t *devmap_device_find_handle(dev map_handle_t handle)277 static devmap_device_t *devmap_device_find_handle(dev_handle_t handle) 280 278 { 281 279 link_t *item; 282 283 assert(fibril_mutex_is_locked(&devices_list_mutex));284 285 280 for (item = devices_list.next; item != &devices_list; item = item->next) { 286 281 devmap_device_t *device = … … 293 288 } 294 289 295 /** Create a namespace (if not already present). */ 290 /** Create a namespace (if not already present) 291 * 292 * The devices_list_mutex should be already held when 293 * calling this function. 294 * 295 */ 296 296 static devmap_namespace_t *devmap_namespace_create(const char *ns_name) 297 297 { 298 devmap_namespace_t *namespace; 299 300 assert(fibril_mutex_is_locked(&devices_list_mutex)); 301 302 namespace = devmap_namespace_find_name(ns_name); 298 devmap_namespace_t *namespace = devmap_namespace_find_name(ns_name); 303 299 if (namespace != NULL) 304 300 return namespace; … … 325 321 } 326 322 327 /** Destroy a namespace (if it is no longer needed). */ 323 /** Destroy a namespace (if it is no longer needed) 324 * 325 * The devices_list_mutex should be already held when 326 * calling this function. 327 * 328 */ 328 329 static void devmap_namespace_destroy(devmap_namespace_t *namespace) 329 330 { 330 assert(fibril_mutex_is_locked(&devices_list_mutex));331 332 331 if (namespace->refcnt == 0) { 333 332 list_remove(&(namespace->namespaces)); … … 338 337 } 339 338 340 /** Increase namespace reference count by including device. */ 339 /** Increase namespace reference count by including device 340 * 341 * The devices_list_mutex should be already held when 342 * calling this function. 343 * 344 */ 341 345 static void devmap_namespace_addref(devmap_namespace_t *namespace, 342 346 devmap_device_t *device) 343 347 { 344 assert(fibril_mutex_is_locked(&devices_list_mutex));345 346 348 device->namespace = namespace; 347 349 namespace->refcnt++; 348 350 } 349 351 350 /** Decrease namespace reference count. */ 352 /** Decrease namespace reference count 353 * 354 * The devices_list_mutex should be already held when 355 * calling this function. 356 * 357 */ 351 358 static void devmap_namespace_delref(devmap_namespace_t *namespace) 352 359 { 353 assert(fibril_mutex_is_locked(&devices_list_mutex));354 355 360 namespace->refcnt--; 356 361 devmap_namespace_destroy(namespace); 357 362 } 358 363 359 /** Unregister device and free it. */ 364 /** Unregister device and free it 365 * 366 * The devices_list_mutex should be already held when 367 * calling this function. 368 * 369 */ 360 370 static void devmap_device_unregister_core(devmap_device_t *device) 361 371 { 362 assert(fibril_mutex_is_locked(&devices_list_mutex));363 364 372 devmap_namespace_delref(device->namespace); 365 373 list_remove(&(device->devices)); … … 379 387 ipc_callid_t iid = async_get_call(&icall); 380 388 381 if (IPC_GET_ IMETHOD(icall) != DEVMAP_DRIVER_REGISTER) {382 async_answer_0(iid, EREFUSED);389 if (IPC_GET_METHOD(icall) != DEVMAP_DRIVER_REGISTER) { 390 ipc_answer_0(iid, EREFUSED); 383 391 return NULL; 384 392 } … … 387 395 (devmap_driver_t *) malloc(sizeof(devmap_driver_t)); 388 396 if (driver == NULL) { 389 async_answer_0(iid, ENOMEM);397 ipc_answer_0(iid, ENOMEM); 390 398 return NULL; 391 399 } … … 398 406 if (rc != EOK) { 399 407 free(driver); 400 async_answer_0(iid, rc);408 ipc_answer_0(iid, rc); 401 409 return NULL; 402 410 } … … 408 416 ipc_callid_t callid = async_get_call(&call); 409 417 410 if (IPC_GET_ IMETHOD(call) != IPC_M_CONNECT_TO_ME) {418 if (IPC_GET_METHOD(call) != IPC_M_CONNECT_TO_ME) { 411 419 free(driver->name); 412 420 free(driver); 413 async_answer_0(callid, ENOTSUP);414 async_answer_0(iid, ENOTSUP);421 ipc_answer_0(callid, ENOTSUP); 422 ipc_answer_0(iid, ENOTSUP); 415 423 return NULL; 416 424 } 417 425 418 426 driver->phone = IPC_GET_ARG5(call); 419 async_answer_0(callid, EOK);427 ipc_answer_0(callid, EOK); 420 428 421 429 /* … … 429 437 */ 430 438 list_initialize(&driver->devices); 431 432 link_initialize(&driver->drivers); 439 list_initialize(&(driver->drivers)); 433 440 434 441 fibril_mutex_lock(&drivers_list_mutex); … … 445 452 fibril_mutex_unlock(&drivers_list_mutex); 446 453 447 async_answer_0(iid, EOK);454 ipc_answer_0(iid, EOK); 448 455 449 456 return driver; … … 463 470 464 471 if (driver->phone != 0) 465 async_hangup(driver->phone);472 ipc_hangup(driver->phone); 466 473 467 474 /* Remove it from list of drivers */ … … 498 505 { 499 506 if (driver == NULL) { 500 async_answer_0(iid, EREFUSED);507 ipc_answer_0(iid, EREFUSED); 501 508 return; 502 509 } … … 506 513 (devmap_device_t *) malloc(sizeof(devmap_device_t)); 507 514 if (device == NULL) { 508 async_answer_0(iid, ENOMEM); 509 return; 510 } 511 512 /* Set the interface, if any. */ 513 device->forward_interface = IPC_GET_ARG1(*icall); 514 515 ipc_answer_0(iid, ENOMEM); 516 return; 517 } 518 515 519 /* Get fqdn */ 516 520 char *fqdn; … … 519 523 if (rc != EOK) { 520 524 free(device); 521 async_answer_0(iid, rc);525 ipc_answer_0(iid, rc); 522 526 return; 523 527 } … … 527 531 free(fqdn); 528 532 free(device); 529 async_answer_0(iid, EINVAL);533 ipc_answer_0(iid, EINVAL); 530 534 return; 531 535 } … … 541 545 free(device->name); 542 546 free(device); 543 async_answer_0(iid, ENOMEM);544 return; 545 } 546 547 li nk_initialize(&device->devices);548 li nk_initialize(&device->driver_devices);547 ipc_answer_0(iid, ENOMEM); 548 return; 549 } 550 551 list_initialize(&(device->devices)); 552 list_initialize(&(device->driver_devices)); 549 553 550 554 /* Check that device is not already registered */ 551 555 if (devmap_device_find_name(namespace->name, device->name) != NULL) { 552 556 printf("%s: Device '%s/%s' already registered\n", NAME, 553 namespace->name, device->name);557 device->namespace, device->name); 554 558 devmap_namespace_destroy(namespace); 555 559 fibril_mutex_unlock(&devices_list_mutex); 556 560 free(device->name); 557 561 free(device); 558 async_answer_0(iid, EEXISTS);562 ipc_answer_0(iid, EEXISTS); 559 563 return; 560 564 } … … 562 566 /* Get unique device handle */ 563 567 device->handle = devmap_create_handle(); 564 568 565 569 devmap_namespace_addref(namespace, device); 566 570 device->driver = driver; … … 578 582 fibril_mutex_unlock(&devices_list_mutex); 579 583 580 async_answer_1(iid, EOK, device->handle);584 ipc_answer_1(iid, EOK, device->handle); 581 585 } 582 586 … … 604 608 * Get handle from request 605 609 */ 606 dev map_handle_t handle = IPC_GET_ARG2(*call);610 dev_handle_t handle = IPC_GET_ARG2(*call); 607 611 devmap_device_t *dev = devmap_device_find_handle(handle); 608 612 609 613 if ((dev == NULL) || (dev->driver == NULL) || (dev->driver->phone == 0)) { 610 614 fibril_mutex_unlock(&devices_list_mutex); 611 async_answer_0(callid, ENOENT); 612 return; 613 } 614 615 if (dev->forward_interface == 0) { 616 async_forward_fast(callid, dev->driver->phone, 617 dev->handle, 0, 0, 618 IPC_FF_NONE); 619 } else { 620 async_forward_fast(callid, dev->driver->phone, 621 dev->forward_interface, dev->handle, 0, 622 IPC_FF_NONE); 623 } 615 ipc_answer_0(callid, ENOENT); 616 return; 617 } 618 619 ipc_forward_fast(callid, dev->driver->phone, dev->handle, 620 IPC_GET_ARG3(*call), 0, IPC_FF_NONE); 624 621 625 622 fibril_mutex_unlock(&devices_list_mutex); … … 640 637 DEVMAP_NAME_MAXLEN, 0, NULL); 641 638 if (rc != EOK) { 642 async_answer_0(iid, rc);639 ipc_answer_0(iid, rc); 643 640 return; 644 641 } … … 648 645 if (!devmap_fqdn_split(fqdn, &ns_name, &name)) { 649 646 free(fqdn); 650 async_answer_0(iid, EINVAL);647 ipc_answer_0(iid, EINVAL); 651 648 return; 652 649 } … … 675 672 } 676 673 677 async_answer_0(iid, ENOENT);674 ipc_answer_0(iid, ENOENT); 678 675 free(ns_name); 679 676 free(name); … … 682 679 } 683 680 684 async_answer_1(iid, EOK, dev->handle);681 ipc_answer_1(iid, EOK, dev->handle); 685 682 686 683 fibril_mutex_unlock(&devices_list_mutex); … … 703 700 DEVMAP_NAME_MAXLEN, 0, NULL); 704 701 if (rc != EOK) { 705 async_answer_0(iid, rc);702 ipc_answer_0(iid, rc); 706 703 return; 707 704 } … … 728 725 } 729 726 730 async_answer_0(iid, ENOENT);727 ipc_answer_0(iid, ENOENT); 731 728 free(name); 732 729 fibril_mutex_unlock(&devices_list_mutex); … … 734 731 } 735 732 736 async_answer_1(iid, EOK, namespace->handle);733 ipc_answer_1(iid, EOK, namespace->handle); 737 734 738 735 fibril_mutex_unlock(&devices_list_mutex); … … 750 747 devmap_device_find_handle(IPC_GET_ARG1(*icall)); 751 748 if (dev == NULL) 752 async_answer_1(iid, EOK, DEV_HANDLE_NONE);749 ipc_answer_1(iid, EOK, DEV_HANDLE_NONE); 753 750 else 754 async_answer_1(iid, EOK, DEV_HANDLE_DEVICE);751 ipc_answer_1(iid, EOK, DEV_HANDLE_DEVICE); 755 752 } else 756 async_answer_1(iid, EOK, DEV_HANDLE_NAMESPACE);753 ipc_answer_1(iid, EOK, DEV_HANDLE_NAMESPACE); 757 754 758 755 fibril_mutex_unlock(&devices_list_mutex); … … 762 759 { 763 760 fibril_mutex_lock(&devices_list_mutex); 764 async_answer_1(iid, EOK, list_count(&namespaces_list));761 ipc_answer_1(iid, EOK, list_count(&namespaces_list)); 765 762 fibril_mutex_unlock(&devices_list_mutex); 766 763 } … … 773 770 devmap_namespace_find_handle(IPC_GET_ARG1(*icall)); 774 771 if (namespace == NULL) 775 async_answer_0(iid, EEXISTS);772 ipc_answer_0(iid, EEXISTS); 776 773 else 777 async_answer_1(iid, EOK, namespace->refcnt);774 ipc_answer_1(iid, EOK, namespace->refcnt); 778 775 779 776 fibril_mutex_unlock(&devices_list_mutex); … … 785 782 size_t size; 786 783 if (!async_data_read_receive(&callid, &size)) { 787 async_answer_0(callid, EREFUSED);788 async_answer_0(iid, EREFUSED);784 ipc_answer_0(callid, EREFUSED); 785 ipc_answer_0(iid, EREFUSED); 789 786 return; 790 787 } 791 788 792 789 if ((size % sizeof(dev_desc_t)) != 0) { 793 async_answer_0(callid, EINVAL);794 async_answer_0(iid, EINVAL);790 ipc_answer_0(callid, EINVAL); 791 ipc_answer_0(iid, EINVAL); 795 792 return; 796 793 } … … 801 798 if (count != list_count(&namespaces_list)) { 802 799 fibril_mutex_unlock(&devices_list_mutex); 803 async_answer_0(callid, EOVERFLOW);804 async_answer_0(iid, EOVERFLOW);800 ipc_answer_0(callid, EOVERFLOW); 801 ipc_answer_0(iid, EOVERFLOW); 805 802 return; 806 803 } … … 809 806 if (desc == NULL) { 810 807 fibril_mutex_unlock(&devices_list_mutex); 811 async_answer_0(callid, ENOMEM);812 async_answer_0(iid, ENOMEM);808 ipc_answer_0(callid, ENOMEM); 809 ipc_answer_0(iid, ENOMEM); 813 810 return; 814 811 } … … 826 823 } 827 824 828 sysarg_t retval = async_data_read_finalize(callid, desc, size);825 ipcarg_t retval = async_data_read_finalize(callid, desc, size); 829 826 830 827 free(desc); 831 828 fibril_mutex_unlock(&devices_list_mutex); 832 829 833 async_answer_0(iid, retval);830 ipc_answer_0(iid, retval); 834 831 } 835 832 … … 842 839 size_t size; 843 840 if (!async_data_read_receive(&callid, &size)) { 844 async_answer_0(callid, EREFUSED);845 async_answer_0(iid, EREFUSED);841 ipc_answer_0(callid, EREFUSED); 842 ipc_answer_0(iid, EREFUSED); 846 843 return; 847 844 } 848 845 849 846 if ((size % sizeof(dev_desc_t)) != 0) { 850 async_answer_0(callid, EINVAL);851 async_answer_0(iid, EINVAL);847 ipc_answer_0(callid, EINVAL); 848 ipc_answer_0(iid, EINVAL); 852 849 return; 853 850 } … … 859 856 if (namespace == NULL) { 860 857 fibril_mutex_unlock(&devices_list_mutex); 861 async_answer_0(callid, ENOENT);862 async_answer_0(iid, ENOENT);858 ipc_answer_0(callid, ENOENT); 859 ipc_answer_0(iid, ENOENT); 863 860 return; 864 861 } … … 867 864 if (count != namespace->refcnt) { 868 865 fibril_mutex_unlock(&devices_list_mutex); 869 async_answer_0(callid, EOVERFLOW);870 async_answer_0(iid, EOVERFLOW);866 ipc_answer_0(callid, EOVERFLOW); 867 ipc_answer_0(iid, EOVERFLOW); 871 868 return; 872 869 } … … 875 872 if (desc == NULL) { 876 873 fibril_mutex_unlock(&devices_list_mutex); 877 async_answer_0(callid, ENOMEM);878 async_answer_0(iid, EREFUSED);874 ipc_answer_0(callid, ENOMEM); 875 ipc_answer_0(iid, EREFUSED); 879 876 return; 880 877 } … … 893 890 } 894 891 895 sysarg_t retval = async_data_read_finalize(callid, desc, size);892 ipcarg_t retval = async_data_read_finalize(callid, desc, size); 896 893 897 894 free(desc); 898 895 fibril_mutex_unlock(&devices_list_mutex); 899 896 900 async_answer_0(iid, retval);897 ipc_answer_0(iid, retval); 901 898 } 902 899 … … 917 914 if (!fnd) { 918 915 fibril_mutex_unlock(&null_devices_mutex); 919 async_answer_0(iid, ENOMEM);916 ipc_answer_0(iid, ENOMEM); 920 917 return; 921 918 } … … 927 924 if (dev_name == NULL) { 928 925 fibril_mutex_unlock(&null_devices_mutex); 929 async_answer_0(iid, ENOMEM);926 ipc_answer_0(iid, ENOMEM); 930 927 return; 931 928 } … … 935 932 if (device == NULL) { 936 933 fibril_mutex_unlock(&null_devices_mutex); 937 async_answer_0(iid, ENOMEM);934 ipc_answer_0(iid, ENOMEM); 938 935 return; 939 936 } … … 945 942 fibril_mutex_lock(&devices_list_mutex); 946 943 fibril_mutex_unlock(&null_devices_mutex); 947 async_answer_0(iid, ENOMEM);948 return; 949 } 950 951 li nk_initialize(&device->devices);952 li nk_initialize(&device->driver_devices);944 ipc_answer_0(iid, ENOMEM); 945 return; 946 } 947 948 list_initialize(&(device->devices)); 949 list_initialize(&(device->driver_devices)); 953 950 954 951 /* Get unique device handle */ … … 959 956 device->name = dev_name; 960 957 961 /* 962 * Insert device into list of all devices and into null devices array. 963 * Insert device into a dummy list of null driver's devices so that it 964 * can be safely removed later. 965 */ 958 /* Insert device into list of all devices 959 and into null devices array */ 966 960 list_append(&device->devices, &devices_list); 967 list_append(&device->driver_devices, &dummy_null_driver_devices);968 961 null_devices[i] = device; 969 962 … … 971 964 fibril_mutex_unlock(&null_devices_mutex); 972 965 973 async_answer_1(iid, EOK, (sysarg_t) i);966 ipc_answer_1(iid, EOK, (ipcarg_t) i); 974 967 } 975 968 976 969 static void devmap_null_destroy(ipc_callid_t iid, ipc_call_t *icall) 977 970 { 978 sysarg_t i = IPC_GET_ARG1(*icall);971 ipcarg_t i = IPC_GET_ARG1(*icall); 979 972 if (i >= NULL_DEVICES) { 980 async_answer_0(iid, ELIMIT);973 ipc_answer_0(iid, ELIMIT); 981 974 return; 982 975 } … … 986 979 if (null_devices[i] == NULL) { 987 980 fibril_mutex_unlock(&null_devices_mutex); 988 async_answer_0(iid, ENOENT);981 ipc_answer_0(iid, ENOENT); 989 982 return; 990 983 } … … 997 990 998 991 fibril_mutex_unlock(&null_devices_mutex); 999 async_answer_0(iid, EOK);992 ipc_answer_0(iid, EOK); 1000 993 } 1001 994 … … 1023 1016 { 1024 1017 /* Accept connection */ 1025 async_answer_0(iid, EOK);1018 ipc_answer_0(iid, EOK); 1026 1019 1027 1020 devmap_driver_t *driver = devmap_driver_register(); … … 1034 1027 ipc_callid_t callid = async_get_call(&call); 1035 1028 1036 switch (IPC_GET_ IMETHOD(call)) {1029 switch (IPC_GET_METHOD(call)) { 1037 1030 case IPC_M_PHONE_HUNGUP: 1038 1031 cont = false; … … 1040 1033 case DEVMAP_DRIVER_UNREGISTER: 1041 1034 if (NULL == driver) 1042 async_answer_0(callid, ENOENT);1035 ipc_answer_0(callid, ENOENT); 1043 1036 else 1044 async_answer_0(callid, EOK);1037 ipc_answer_0(callid, EOK); 1045 1038 break; 1046 1039 case DEVMAP_DEVICE_REGISTER: … … 1059 1052 break; 1060 1053 default: 1061 async_answer_0(callid, ENOENT); 1054 if (!(callid & IPC_CALLID_NOTIFICATION)) 1055 ipc_answer_0(callid, ENOENT); 1062 1056 } 1063 1057 } … … 1078 1072 { 1079 1073 /* Accept connection */ 1080 async_answer_0(iid, EOK);1074 ipc_answer_0(iid, EOK); 1081 1075 1082 1076 bool cont = true; … … 1085 1079 ipc_callid_t callid = async_get_call(&call); 1086 1080 1087 switch (IPC_GET_ IMETHOD(call)) {1081 switch (IPC_GET_METHOD(call)) { 1088 1082 case IPC_M_PHONE_HUNGUP: 1089 1083 cont = false; … … 1117 1111 break; 1118 1112 default: 1119 async_answer_0(callid, ENOENT); 1113 if (!(callid & IPC_CALLID_NOTIFICATION)) 1114 ipc_answer_0(callid, ENOENT); 1120 1115 } 1121 1116 } … … 1128 1123 { 1129 1124 /* Select interface */ 1130 switch (( sysarg_t) (IPC_GET_ARG1(*icall))) {1125 switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) { 1131 1126 case DEVMAP_DRIVER: 1132 1127 devmap_connection_driver(iid, icall); … … 1141 1136 default: 1142 1137 /* No such interface */ 1143 async_answer_0(iid, ENOENT);1138 ipc_answer_0(iid, ENOENT); 1144 1139 } 1145 1140 } … … 1161 1156 1162 1157 /* Register device mapper at naming service */ 1163 if (service_register(SERVICE_DEVMAP) != EOK) 1158 ipcarg_t phonead; 1159 if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAP, 0, 0, &phonead) != 0) 1164 1160 return -1; 1165 1161
Note:
See TracChangeset
for help on using the changeset viewer.