--- devel/glib20/Makefile (revision 425755) +++ devel/glib20/Makefile (working copy) @@ -3,7 +3,7 @@ PORTNAME= glib PORTVERSION= 2.46.2 -PORTREVISION= 3 +PORTREVISION= 4 CATEGORIES= devel MASTER_SITES= GNOME DIST_SUBDIR= gnome2 @@ -110,6 +110,23 @@ @${REINPLACE_CMD} -e 's|inotify_support=yes|inotify_support=no| ; \ s|-Werror|| ; \ s|#define HAVE_SYS_INOTIFY_H 1||' ${WRKSRC}/configure + @${CP} ${FILESDIR}/kqueue_fnm.c.in ${WRKSRC}/gio/kqueue/kqueue_fnm.c + @${CP} ${FILESDIR}/kqueue_fnm.h.in ${WRKSRC}/gio/kqueue/kqueue_fnm.h + @${RM} ${WRKSRC}/gio/kqueue/dep-list.c + @${RM} ${WRKSRC}/gio/kqueue/dep-list.h + @${RM} ${WRKSRC}/gio/kqueue/gkqueuefilemonitor.h + @${RM} ${WRKSRC}/gio/kqueue/kqueue-exclusions.c + @${RM} ${WRKSRC}/gio/kqueue/kqueue-exclusions.h + @${RM} ${WRKSRC}/gio/kqueue/kqueue-helper.c + @${RM} ${WRKSRC}/gio/kqueue/kqueue-helper.h + @${RM} ${WRKSRC}/gio/kqueue/kqueue-missing.c + @${RM} ${WRKSRC}/gio/kqueue/kqueue-missing.h + @${RM} ${WRKSRC}/gio/kqueue/kqueue-sub.c + @${RM} ${WRKSRC}/gio/kqueue/kqueue-sub.h + @${RM} ${WRKSRC}/gio/kqueue/kqueue-thread.c + @${RM} ${WRKSRC}/gio/kqueue/kqueue-thread.h + @${RM} ${WRKSRC}/gio/kqueue/kqueue-utils.c + @${RM} ${WRKSRC}/gio/kqueue/kqueue-utils.h post-install: @${MKDIR} ${STAGEDIR}${PREFIX}/share/GConf/gsettings --- devel/glib20/files/kqueue_fnm.c.in (nonexistent) +++ devel/glib20/files/kqueue_fnm.c.in (working copy) @@ -0,0 +1,536 @@ +/*- + * Copyright (c) 2016 Rozhuk Ivan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Author: Rozhuk Ivan + * + */ + +#include +#include +#include +#include +#include /* open, fcntl */ + +#include +#include /* malloc, exit */ +#include /* close, write, sysconf */ +#include /* bcopy, bzero, memcpy, memmove, memset, strerror... */ +#include +#include // opendir, readdir + +#include "kqueue_fnm.h" + + +/* Preallocate items count. */ +#ifndef FILES_ALLOC_BLK_SIZE +# define FILES_ALLOC_BLK_SIZE 8 +#endif + +typedef struct file_info_s { /* Directory file. */ + struct dirent de; /* d_reclen used for action. */ + struct stat sb; +} file_info_t, *file_info_p; + + +typedef struct kqueue_file_mon_data_s { + int fd; /* fd for notify kqueue(). */ + int is_dir; + char path[(PATH_MAX + 2)]; + size_t path_size; + size_t ppath_size; /* Parent path size. */ + void *udata; + /* For dir. */ + file_info_p files; + volatile size_t files_count; + size_t files_allocated; +} kqueue_file_mon_data_t; + + +typedef struct kqueue_file_nonify_monitor_s { + int fd; /* kqueue() fd. */ +} kqueue_fnm_t; + + +#ifdef O_EVTONLY +# define OPEN_FILE_FLAGS O_EVTONLY +#else +# define OPEN_FILE_FLAGS (O_RDONLY | O_NOFOLLOW) +#endif + +#define IS_NAME_DOTS(__name) ((__name)[0] == '.' && \ + ((__name)[1] == '\0' || \ + ((__name)[1] == '.' && (__name)[2] == '\0'))) +#define zalloc(__size) calloc(1, (__size)) + + +static inline int +mem_cmpn(const void *buf1, const size_t buf1_size, + const void *buf2, const size_t buf2_size) { + + if (buf1_size != buf2_size) + return (((buf1_size > buf2_size) ? 127 : -127)); + if (0 == buf1_size || buf1 == buf2) + return (0); + if (NULL == buf1) + return (-127); + if (NULL == buf2) + return (127); + return (memcmp(buf1, buf2, buf1_size)); +} + +#if !defined(__FreeBSD_version) || __FreeBSD_version < 1100000 +static inline void * +reallocarray(void *buf, const size_t nmemb, const size_t size) { + size_t nmemb_size; + + nmemb_size = (nmemb * size); + if (0 == nmemb_size) { + if (0 != nmemb && + 0 != size) { /* Overflow. */ + errno = ENOMEM; + return (NULL); + } + nmemb_size ++; + } else if (((nmemb | size) & (SIZE_T_MAX << (sizeof(size_t) * 4))) && + (nmemb_size / size) != nmemb) { /* size_t overflow. */ + errno = ENOMEM; + return (NULL); + } + return (realloc(buf, nmemb_size)); +} +#endif + +static inline int +realloc_items(void **items, const size_t item_size, + size_t *allocated, const size_t alloc_blk_cnt, const size_t count) { + size_t allocated_prev, allocated_new; + uint8_t *items_new; + + if (NULL == items || NULL == allocated || 0 == alloc_blk_cnt) + return (EINVAL); + allocated_prev = (*allocated); + if (NULL != (*items) && + allocated_prev > count && + allocated_prev <= (count + alloc_blk_cnt)) + return (0); + allocated_new = (((count / alloc_blk_cnt) + 1) * alloc_blk_cnt); + items_new = (uint8_t*)reallocarray((*items), item_size, allocated_new); + if (NULL == items_new) /* Realloc fail! */ + return (ENOMEM); + if (allocated_new > allocated_prev) { /* Init new mem. */ + bzero((items_new + (allocated_prev * item_size)), + ((allocated_new - allocated_prev) * item_size)); + } + (*items) = items_new; + (*allocated) = allocated_new; + + return (0); +} + + +static void +kqueue_file_mon_data_clean(kqueue_file_mon_data_p fmd) { + + if (NULL == fmd) + return; + close(fmd->fd); + fmd->fd = -1; + free(fmd->files); + fmd->files = NULL; + fmd->files_count = 0; + fmd->files_allocated = 0; +} + +static void +kqueue_file_mon_data_free(kqueue_file_mon_data_p fmd) { + + if (NULL == fmd) + return; + close(fmd->fd); + free(fmd->files); + free(fmd); +} + +static kqueue_file_mon_data_p +kqueue_file_mon_data_alloc(const char *path, void *udata) { + kqueue_file_mon_data_p fmd; + DIR *d = NULL; + struct dirent *de; + struct stat sb; + + if (NULL == path) + return (NULL); + fmd = zalloc(sizeof(kqueue_file_mon_data_t)); + if (NULL == fmd) + return (NULL); + fmd->fd = open(path, OPEN_FILE_FLAGS); + if (-1 == fmd->fd) + goto err_out; + if (0 != fstat(fmd->fd, &sb)) + goto err_out; + /* Remember args. */ + fmd->is_dir = (S_ISDIR(sb.st_mode) ? 1 : 0); + fmd->path_size = strlcpy(fmd->path, path, sizeof(fmd->path)); + fmd->udata = udata; + + /* Get parent filder name. */ + if (fmd->is_dir) { + /* Be sure that folder contain trailing '/'. */ + if ('/' != fmd->path[(fmd->path_size - 1)]) { + fmd->path[fmd->path_size] = '/'; + fmd->path[(fmd->path_size + 1)] = 0; + fmd->path_size ++; + } + /* Skip last '/' for parent dir search. */ + fmd->ppath_size = (fmd->path_size - 1); + } else { + fmd->ppath_size = fmd->path_size; + } + /* Find parent dir path size. */ + while (0 < fmd->ppath_size && '/' != fmd->path[(fmd->ppath_size - 1)]) { + fmd->ppath_size --; + } + + /* Dir special processing. */ + if (0 == fmd->is_dir) + return (fmd); + /* Read and remember dir content. */ + d = opendir(path); + if (NULL == d) + goto err_out; + for (;;) { + if (0 != realloc_items((void**)&fmd->files, sizeof(file_info_t), + &fmd->files_allocated, FILES_ALLOC_BLK_SIZE, fmd->files_count)) + goto err_out; + /* Get file name from folder. */ + if (0 != readdir_r(d, &fmd->files[fmd->files_count].de, &de)) + break; + if (NULL == de) + break; + if (IS_NAME_DOTS(de->d_name)) + continue; + /* Get file attrs. */ + if (0 != fstatat(fmd->fd, de->d_name, + &fmd->files[fmd->files_count].sb, + AT_SYMLINK_NOFOLLOW)) { + bzero(&fmd->files[fmd->files_count].sb, sizeof(struct stat)); + } + fmd->files_count ++; + } + closedir(d); + return (fmd); + +err_out: + if (NULL != d) { + closedir(d); + } + kqueue_file_mon_data_free(fmd); + return (NULL); +} + + +static inline int +kqueue_file_mon_data_find_de(kqueue_file_mon_data_p fmd, struct dirent *de, + size_t *idx) { + size_t i; + + if (NULL == fmd || NULL == de || NULL == idx) + return (0); + for (i = 0; i < fmd->files_count; i ++) { + if (de->d_fileno != fmd->files[i].de.d_fileno || + de->d_type != fmd->files[i].de.d_type) + continue; + (*idx) = i; + return (1); + } + (*idx) = fmd->files_count; + return (0); +} + +static void +kqueue_handle_changes(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd, + kfnm_event_handler_cb cb_func) { + DIR *d; + struct dirent *de; + file_info_t fi; + size_t i, j, k; + + if (NULL == kfnm || NULL == fmd || NULL == cb_func) + return; + if (0 != fstat(fmd->fd, &fi.sb) || + 0 == fi.sb.st_nlink) { + kqueue_file_mon_data_clean(fmd); + cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED, + fmd->path, "", NULL); + return; + } + if (0 == fmd->is_dir) { + fmd->path[fmd->ppath_size] = 0; + cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CHANGED, fmd->path, + (fmd->path + fmd->ppath_size), NULL); + fmd->path[fmd->ppath_size] = '/'; + return; + } + /* Dir processing. */ + d = opendir(fmd->path); + if (NULL == d) + return; + /* Mark all as removed. */ + for (i = 0; i < fmd->files_count; i ++) { + fmd->files[i].de.d_reclen = KF_EVENT_DELETED; + } + + /* Update dir. */ + while (0 == readdir_r(d, &fi.de, &de)) { + if (NULL == de) + break; + if (IS_NAME_DOTS(de->d_name)) + continue; + /* Get file stat. */ + if (0 != fstatat(fmd->fd, de->d_name, &fi.sb, AT_SYMLINK_NOFOLLOW)) { + bzero(&fi.sb, sizeof(struct stat)); /* Fail, set to zero. */ + } + /* Is new file/folder? */ + if (0 == kqueue_file_mon_data_find_de(fmd, de, &i)) { /* Add new. */ + if (0 != realloc_items((void**)&fmd->files, sizeof(file_info_t), + &fmd->files_allocated, FILES_ALLOC_BLK_SIZE, fmd->files_count)) + goto err_out; + memcpy(&fmd->files[fmd->files_count], &fi, sizeof(file_info_t)); + fmd->files[fmd->files_count].de.d_reclen = KF_EVENT_CREATED; + fmd->files_count ++; + /* Notify. */ + cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CREATED, + fmd->path, de->d_name, NULL); + continue; + } + /* Is renamed? */ + if (0 != mem_cmpn(de->d_name, de->d_namlen, + fmd->files[i].de.d_name, fmd->files[i].de.d_namlen)) { + /* Notify. */ + cb_func(kfnm, fmd, fmd->udata, KF_EVENT_RENAMED, + fmd->path, fmd->files[i].de.d_name, de->d_name); + /* Update. */ + memcpy(&fmd->files[i], &fi, sizeof(file_info_t)); + fmd->files[i].de.d_reclen = KF_EVENT_RENAMED; + continue; + } + /* Is modified? */ + if (0 != memcmp(&fmd->files[i].sb, &fi.sb, sizeof(struct stat))) { + memcpy(&fmd->files[i].sb, &fi.sb, sizeof(struct stat)); /* Update stat. */ + fmd->files[i].de.d_reclen = KF_EVENT_CHANGED; + /* Notify. */ + cb_func(kfnm, fmd, fmd->udata, KF_EVENT_CHANGED, + fmd->path, de->d_name, NULL); + continue; + } + /* Not changed. */ + fmd->files[i].de.d_reclen = KF_EVENT_NOT_CHANGED; + } + /* Remove marked as removed. */ + for (i = 0; i < fmd->files_count; i ++) { + if (KF_EVENT_DELETED != fmd->files[i].de.d_reclen) + continue; + /* Look for next non removed item + notify. */ + for (j = (i + 1); j < fmd->files_count; j ++) { + if (KF_EVENT_DELETED != fmd->files[j].de.d_reclen) + break; + } + /* Notify. */ + for (k = i; k < j; k ++) { + cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED, + fmd->path, fmd->files[k].de.d_name, NULL); + } + memmove(&fmd->files[i], &fmd->files[j], + (sizeof(file_info_t) * (fmd->files_count - j))); + fmd->files_count -= (j - i); + } + + +err_out: + if (NULL != d) { + closedir(d); + } + return; +} + +static void +kqueue_handle_rename(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd, + kfnm_event_handler_cb cb_func) { + DIR *d; + struct dirent *de, dedat; + struct stat sb; + char old_filename[(MAXNAMLEN + 2)]; + size_t old_filename_size; + + if (NULL == kfnm || NULL == fmd || NULL == cb_func) + return; + if (0 != fstat(fmd->fd, &sb) || + 0 == sb.st_nlink) { +notify_removed: + kqueue_file_mon_data_clean(fmd); + cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED, + fmd->path, "", NULL); + return; + } + /* Save old file name. */ + old_filename_size = (fmd->path_size - fmd->ppath_size - fmd->is_dir); + memcpy(old_filename, + (fmd->path + fmd->ppath_size), + old_filename_size); + old_filename[old_filename_size] = 0; + + /* Get parent folder name. */ + fmd->path[fmd->ppath_size] = 0; + /* Try to open. */ + d = opendir(fmd->path); + /* Restore '/' after parent folder. */ + fmd->path[fmd->ppath_size] = '/'; + if (NULL == d) + return; + /* Find new name by inode. */ + while (0 == readdir_r(d, &dedat, &de)) { + if (NULL == de) + break; + if (de->d_fileno == sb.st_ino) + break; + } + closedir(d); + if (NULL == de) + goto notify_removed; /* Not found. */ + /* Update name. */ + if ((sizeof(fmd->path) - 2) <= (fmd->ppath_size + de->d_namlen)) + return; /* Too long. */ + memcpy((fmd->path + fmd->ppath_size), de->d_name, de->d_namlen); + fmd->path_size = (fmd->ppath_size + de->d_namlen); + /* Add last '/' for dir. */ + fmd->path[fmd->path_size] = '/'; + fmd->path_size += fmd->is_dir; + fmd->path[fmd->path_size] = 0; + /* Notify. */ + cb_func(kfnm, fmd, fmd->udata, KF_EVENT_RENAMED, + fmd->path, old_filename, de->d_name); +} + + +void +kqueue_fnm_free(kqueue_fnm_p kfnm) { + + if (NULL == kfnm) + return; + close(kfnm->fd); + kfnm->fd = -1; + free(kfnm); +} + +kqueue_fnm_p +kqueue_fnm_create(void) { + kqueue_fnm_p kfnm; + + kfnm = zalloc(sizeof(kqueue_fnm_t)); + if (NULL == kfnm) + return (NULL); + kfnm->fd = kqueue(); + if (-1 == kfnm->fd) + goto err_out; + return (kfnm); + +err_out: + kqueue_fnm_free(kfnm); + return (NULL); +} + +kqueue_file_mon_data_p +kqueue_fnm_add(kqueue_fnm_p kfnm, const char *path, void *udata) { + struct kevent kev; + struct timespec ke_timeout; + kqueue_file_mon_data_p fmd; + + if (NULL == kfnm || NULL == path) + return (NULL); + fmd = (void*)kqueue_file_mon_data_alloc(path, udata); + if (NULL == fmd) + return (NULL); + kev.ident = fmd->fd; + kev.filter = EVFILT_VNODE; + kev.flags = (EV_ADD | EV_CLEAR); + kev.fflags = (NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE); + kev.udata = (void*)fmd; + bzero(&ke_timeout, sizeof(ke_timeout)); + if (-1 == kevent(kfnm->fd, &kev, 1, NULL, 0, &ke_timeout)) { + kqueue_file_mon_data_free(fmd); + return (NULL); + } + return (fmd); +} + +void +kqueue_fnm_del(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd) { + + if (NULL == kfnm || NULL == fmd) + return; + kqueue_file_mon_data_free(fmd); +} + + +int +kqueue_fnm_get_ev_recv_fd(kqueue_fnm_p kfnm) { + + if (NULL == kfnm) + return (-1); + return (kfnm->fd); +} + +void +kqueue_fnm_process_events(kqueue_fnm_p kfnm, kfnm_event_handler_cb cb_func) { + struct kevent kev; + struct timespec ke_timeout; + kqueue_file_mon_data_p fmd; + + if (NULL == kfnm || NULL == cb_func) + return; + /* Get and process events. */ + bzero(&ke_timeout, sizeof(ke_timeout)); + while (0 < kevent(kfnm->fd, NULL, 0, &kev, 1, &ke_timeout)) { + if (EVFILT_VNODE != kev.filter || + 0 == kev.udata) + continue; /* Unknown event or no associated data, skip. */ + fmd = (kqueue_file_mon_data_p)kev.udata; + + if (EV_ERROR & kev.flags) { + kev.flags |= NOTE_REVOKE; /* Treat error as unmount. */ + } + if (NOTE_RENAME & kev.fflags) { + kqueue_handle_rename(kfnm, fmd, cb_func); + } + if ((NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB) & kev.fflags) { + kqueue_handle_changes(kfnm, fmd, cb_func); + } + if ((NOTE_DELETE | NOTE_REVOKE) & kev.fflags) { + kqueue_file_mon_data_clean(fmd); + cb_func(kfnm, fmd, fmd->udata, KF_EVENT_DELETED, + fmd->path, "", NULL); + } + } +} --- devel/glib20/files/kqueue_fnm.h.in (nonexistent) +++ devel/glib20/files/kqueue_fnm.h.in (working copy) @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2016 Rozhuk Ivan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Author: Rozhuk Ivan + * + */ + + + +#ifndef __KQUEUE_FILE_NOTIFY_MONITOR_H__ +#define __KQUEUE_FILE_NOTIFY_MONITOR_H__ + +#include +#include +#include + + +typedef struct kqueue_file_nonify_monitor_s *kqueue_fnm_p; +typedef struct kqueue_file_mon_data_s *kqueue_file_mon_data_p; + +typedef void (*kfnm_event_handler_cb)(kqueue_fnm_p kfnm, + kqueue_file_mon_data_p fmd, void *udata, + uint32_t event, + const char *base, + const char *filename, + const char *new_filename); +#define KF_EVENT_NOT_CHANGED 0 /* Internal use. */ +#define KF_EVENT_CREATED 1 +#define KF_EVENT_DELETED 2 +#define KF_EVENT_RENAMED 3 +#define KF_EVENT_CHANGED 4 + + +kqueue_fnm_p kqueue_fnm_create(void); +void kqueue_fnm_free(kqueue_fnm_p kfnm); + +kqueue_file_mon_data_p kqueue_fnm_add(kqueue_fnm_p kfnm, + const char *path, void *udata); +void kqueue_fnm_del(kqueue_fnm_p kfnm, kqueue_file_mon_data_p fmd); + +int kqueue_fnm_get_ev_recv_fd(kqueue_fnm_p kfnm); +void kqueue_fnm_process_events(kqueue_fnm_p kfnm, + kfnm_event_handler_cb cb_func); + + +#endif /* __KQUEUE_FILE_NOTIFY_MONITOR_H__ */ --- devel/glib20/files/patch-gio_kqueue_Makefile.am (nonexistent) +++ devel/glib20/files/patch-gio_kqueue_Makefile.am (working copy) @@ -0,0 +1,26 @@ +--- gio/kqueue/Makefile.am.orig 2015-10-14 14:41:16.000000000 +0300 ++++ gio/kqueue/Makefile.am 2016-11-06 05:08:37.646089000 +0300 +@@ -4,21 +4,8 @@ + + libkqueue_la_SOURCES = \ + gkqueuefilemonitor.c \ +- gkqueuefilemonitor.h \ +- kqueue-helper.c \ +- kqueue-helper.h \ +- kqueue-thread.c \ +- kqueue-thread.h \ +- kqueue-sub.c \ +- kqueue-sub.h \ +- kqueue-missing.c \ +- kqueue-missing.h \ +- kqueue-utils.c \ +- kqueue-utils.h \ +- kqueue-exclusions.c \ +- kqueue-exclusions.h \ +- dep-list.c \ +- dep-list.h \ ++ kqueue_fnm.c \ ++ kqueue_fnm.h \ + $(NULL) + + libkqueue_la_CFLAGS = \ --- devel/glib20/files/patch-gio_kqueue_gkqueuefilemonitor.c (nonexistent) +++ devel/glib20/files/patch-gio_kqueue_gkqueuefilemonitor.c (working copy) @@ -0,0 +1,372 @@ +--- gio/kqueue/gkqueuefilemonitor.c.orig 2015-10-14 14:41:16.000000000 +0300 ++++ gio/kqueue/gkqueuefilemonitor.c 2016-11-06 05:01:49.712277000 +0300 +@@ -1,198 +1,203 @@ +-/******************************************************************************* +- Copyright (c) 2011, 2012 Dmitry Matveev +- +- Permission is hereby granted, free of charge, to any person obtaining a copy +- of this software and associated documentation files (the "Software"), to deal +- in the Software without restriction, including without limitation the rights +- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- copies of the Software, and to permit persons to whom the Software is +- furnished to do so, subject to the following conditions: +- +- The above copyright notice and this permission notice shall be included in +- all copies or substantial portions of the Software. +- +- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- THE SOFTWARE. +-*******************************************************************************/ ++/*- ++ * Copyright (c) 2016 Rozhuk Ivan ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * Author: Rozhuk Ivan ++ * ++ */ + + #include "config.h" + +-#include "gkqueuefilemonitor.h" +-#include "kqueue-helper.h" +-#include "kqueue-exclusions.h" +-#include +-#include ++#include ++#include ++#include ++#include + #include ++#include "glib-private.h" ++#include ++#include "kqueue_fnm.h" + + +-struct _GKqueueFileMonitor +-{ +- GLocalFileMonitor parent_instance; ++static GMutex kqueue_lock; ++static GSource *kqueue_source = NULL; ++static volatile kqueue_fnm_p kqueue_fnm = NULL; + +- kqueue_sub *sub; ++#define G_TYPE_KQUEUE_FILE_MONITOR (g_kqueue_file_monitor_get_type()) ++#define G_KQUEUE_FILE_MONITOR(inst) (G_TYPE_CHECK_INSTANCE_CAST((inst), \ ++ G_TYPE_KQUEUE_FILE_MONITOR, GKqueueFileMonitor)) + +- GFileMonitor *fallback; +- GFile *fbfile; +-}; ++typedef GLocalFileMonitorClass GKqueueFileMonitorClass; + +-static gboolean g_kqueue_file_monitor_cancel (GFileMonitor* monitor); ++typedef struct { ++ GLocalFileMonitor parent_instance; ++ kqueue_file_mon_data_p fmd; ++} GKqueueFileMonitor; + ++GType g_kqueue_file_monitor_get_type(void); + G_DEFINE_TYPE_WITH_CODE (GKqueueFileMonitor, g_kqueue_file_monitor, G_TYPE_LOCAL_FILE_MONITOR, +- g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME, ++ g_io_extension_point_implement(G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME, + g_define_type_id, + "kqueue", +- 20)) ++ 10)) + + + static void +-_fallback_callback (GFileMonitor *unused, +- GFile *first, +- GFile *second, +- GFileMonitorEvent event, +- gpointer udata) +-{ +- GKqueueFileMonitor *kq_mon = G_KQUEUE_FILE_MONITOR (udata); +- GFileMonitor *mon = G_FILE_MONITOR (kq_mon); +- g_assert (kq_mon != NULL); +- g_assert (mon != NULL); +- (void) unused; +- +- if (event == G_FILE_MONITOR_EVENT_CHANGED) +- { +- GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (kq_mon); +- +- _kh_dir_diff (kq_mon->sub, local_monitor->source); +- } +- else +- g_file_monitor_emit_event (mon, first, second, event); +-} +- +- +-static void +-g_kqueue_file_monitor_finalize (GObject *object) +-{ +- GKqueueFileMonitor *kqueue_monitor = G_KQUEUE_FILE_MONITOR (object); +- +- if (kqueue_monitor->sub) +- { +- _kh_cancel_sub (kqueue_monitor->sub); +- _kh_sub_free (kqueue_monitor->sub); +- kqueue_monitor->sub = NULL; +- } +- +- if (kqueue_monitor->fallback) +- g_object_unref (kqueue_monitor->fallback); +- +- if (kqueue_monitor->fbfile) +- g_object_unref (kqueue_monitor->fbfile); +- +- if (G_OBJECT_CLASS (g_kqueue_file_monitor_parent_class)->finalize) +- (*G_OBJECT_CLASS (g_kqueue_file_monitor_parent_class)->finalize) (object); +-} +- +-static void +-g_kqueue_file_monitor_start (GLocalFileMonitor *local_monitor, +- const gchar *dirname, +- const gchar *basename, +- const gchar *filename, +- GFileMonitorSource *source) +-{ +- GKqueueFileMonitor *kqueue_monitor = G_KQUEUE_FILE_MONITOR (local_monitor); +- GObject *obj; +- GKqueueFileMonitorClass *klass; +- GObjectClass *parent_class; +- kqueue_sub *sub = NULL; +- gboolean ret_kh_startup = FALSE; +- const gchar *path = NULL; +- +- +- ret_kh_startup = _kh_startup (); +- g_assert (ret_kh_startup); +- +- path = filename; +- if (!path) +- path = dirname; +- +- /* For a directory monitor, create a subscription object anyway. +- * It will be used for directory diff calculation routines. +- * Wait, directory diff in a GKqueueFileMonitor? +- * Yes, it is. When a file monitor is started on an non-existent +- * file, GIO uses a GKqueueFileMonitor object for that. If a directory +- * will be created under that path, GKqueueFileMonitor will have to +- * handle the directory notifications. */ +- +- sub = _kh_sub_new (path, TRUE, source); +- +- /* FIXME: what to do about errors here? we can't return NULL or another +- * kind of error and an assertion is probably too hard (same issue as in +- * the inotify backend) */ +- g_assert (sub != NULL); +- kqueue_monitor->sub = sub; +- +- if (!_ke_is_excluded (path)) +- _kh_add_sub (sub); +- else +- { +- GFile *file = g_file_new_for_path (path); +- kqueue_monitor->fbfile = file; +- kqueue_monitor->fallback = _g_poll_file_monitor_new (file); +- g_signal_connect (kqueue_monitor->fallback, +- "changed", +- G_CALLBACK (_fallback_callback), +- kqueue_monitor); +- } ++kqueue_event_handler(kqueue_fnm_p kfnm, ++ kqueue_file_mon_data_p fmd, void *udata, uint32_t event, ++ const char *base, const char *filename, const char *new_filename) { ++ static const uint32_t kfnm_to_glib_map[] = { ++ 0, /* KF_EVENT_NOT_CHANGED */ ++ G_FILE_MONITOR_EVENT_CREATED, /* KF_EVENT_CREATED */ ++ G_FILE_MONITOR_EVENT_DELETED, /* KF_EVENT_DELETED */ ++ G_FILE_MONITOR_EVENT_RENAMED, /* KF_EVENT_RENAMED */ ++ G_FILE_MONITOR_EVENT_CHANGED /* KF_EVENT_CHANGED */ ++ }; ++ ++ if (NULL == kfnm || NULL == filename || ++ KF_EVENT_CREATED > event || ++ KF_EVENT_CHANGED < event) ++ return; ++ g_file_monitor_source_handle_event(udata, ++ kfnm_to_glib_map[event], ++ filename, new_filename, NULL, ++ g_source_get_time(kqueue_source)); ++} ++ ++static gboolean ++g_kqueue_file_monitor_callback(gint fd, GIOCondition condition, gpointer user_data) { ++ ++ /* Only one thread process events. */ ++ g_mutex_lock(&kqueue_lock); ++ kqueue_fnm_process_events(kqueue_fnm, kqueue_event_handler); ++ g_mutex_unlock(&kqueue_lock); ++ ++ return (TRUE); ++} ++ ++static gboolean ++g_kqueue_file_monitor_is_supported(void) { ++ ++ if (NULL != kqueue_fnm) ++ return (TRUE); ++ /* Init only once. */ ++ g_mutex_lock(&kqueue_lock); ++ if (NULL != kqueue_fnm) { ++ g_mutex_unlock(&kqueue_lock); ++ return (TRUE); /* Initialized while wait lock. */ ++ } ++ kqueue_fnm = kqueue_fnm_create(); ++ if (NULL == kqueue_fnm) { ++ g_mutex_unlock(&kqueue_lock); ++ return (FALSE); /* Init fail. */ ++ } ++ kqueue_source = g_unix_fd_source_new(kqueue_fnm_get_ev_recv_fd(kqueue_fnm), G_IO_IN); ++ g_source_set_callback(kqueue_source, (GSourceFunc)g_kqueue_file_monitor_callback, NULL, NULL); ++ g_source_attach(kqueue_source, GLIB_PRIVATE_CALL(g_get_worker_context)()); ++ g_mutex_unlock(&kqueue_lock); ++ ++ return (TRUE); + } + + static gboolean +-g_kqueue_file_monitor_is_supported (void) +-{ +- return _kh_startup (); ++g_kqueue_file_monitor_cancel(GFileMonitor *monitor) { ++ GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR(monitor); ++ ++ kqueue_fnm_del(kqueue_fnm, gffm->fmd); ++ gffm->fmd = NULL; ++ ++ return (TRUE); + } + + static void +-g_kqueue_file_monitor_class_init (GKqueueFileMonitorClass *klass) +-{ +- GObjectClass *gobject_class = G_OBJECT_CLASS (klass); +- GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (klass); +- GLocalFileMonitorClass *local_file_monitor_class = G_LOCAL_FILE_MONITOR_CLASS (klass); ++g_kqueue_file_monitor_finalize(GObject *object) { ++ GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR(object); + +- gobject_class->finalize = g_kqueue_file_monitor_finalize; +- file_monitor_class->cancel = g_kqueue_file_monitor_cancel; ++ kqueue_fnm_del(kqueue_fnm, gffm->fmd); ++ gffm->fmd = NULL; ++} + +- local_file_monitor_class->is_supported = g_kqueue_file_monitor_is_supported; +- local_file_monitor_class->start = g_kqueue_file_monitor_start; +- local_file_monitor_class->mount_notify = TRUE; /* TODO: ??? */ ++static void ++g_kqueue_file_monitor_start(GLocalFileMonitor *local_monitor, ++ const gchar *dirname, const gchar *basename, ++ const gchar *filename, GFileMonitorSource *source) { ++ GKqueueFileMonitor *gffm = G_KQUEUE_FILE_MONITOR (local_monitor); ++ ++ g_assert(NULL != kqueue_fnm); ++ g_source_ref((GSource*)source); ++ ++ if (NULL == filename) { ++ filename = dirname; ++ } ++ gffm->fmd = kqueue_fnm_add(kqueue_fnm, filename, source); + } + + static void +-g_kqueue_file_monitor_init (GKqueueFileMonitor *monitor) +-{ ++g_kqueue_file_monitor_init(GKqueueFileMonitor *monitor) { ++ + } + +-static gboolean +-g_kqueue_file_monitor_cancel (GFileMonitor *monitor) +-{ +- GKqueueFileMonitor *kqueue_monitor = G_KQUEUE_FILE_MONITOR (monitor); +- +- if (kqueue_monitor->sub) +- { +- _kh_cancel_sub (kqueue_monitor->sub); +- _kh_sub_free (kqueue_monitor->sub); +- kqueue_monitor->sub = NULL; +- } +- else if (kqueue_monitor->fallback) +- { +- g_signal_handlers_disconnect_by_func (kqueue_monitor->fallback, _fallback_callback, kqueue_monitor); +- g_file_monitor_cancel (kqueue_monitor->fallback); +- } ++static void ++g_kqueue_file_monitor_class_init(GKqueueFileMonitorClass *class) { ++ GObjectClass *gobject_class = G_OBJECT_CLASS(class); ++ GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS(class); ++ ++ class->is_supported = g_kqueue_file_monitor_is_supported; ++ class->start = g_kqueue_file_monitor_start; ++ class->mount_notify = TRUE; /* TODO: ??? */ ++ file_monitor_class->cancel = g_kqueue_file_monitor_cancel; ++ gobject_class->finalize = g_kqueue_file_monitor_finalize; ++} ++ ++static void ++g_kqueue_file_monitor_class_finalize(GKqueueFileMonitorClass *class) { ++ ++} ++ ++void ++g_io_module_load(GIOModule *module) { ++ ++ g_type_module_use(G_TYPE_MODULE(module)); ++ ++ g_io_extension_point_implement(G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME, ++ G_TYPE_KQUEUE_FILE_MONITOR, "kqueue", 10); ++ g_io_extension_point_implement(G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME, ++ G_TYPE_KQUEUE_FILE_MONITOR, "kqueue", 10); ++} ++ ++void ++g_io_module_unload(GIOModule *module) { ++ ++ g_assert_not_reached(); ++} + +- if (G_FILE_MONITOR_CLASS (g_kqueue_file_monitor_parent_class)->cancel) +- (*G_FILE_MONITOR_CLASS (g_kqueue_file_monitor_parent_class)->cancel) (monitor); ++char ** ++g_io_module_query(void) { ++ char *eps[] = { ++ G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME, ++ G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME, ++ NULL ++ }; + +- return TRUE; ++ return (g_strdupv(eps)); + }