/** * General function to allocate and initialize a ZSockAddr structure, * and convert a libc style sockaddr * pointer to our representation. * * @param[in] sa libc sockaddr * pointer to convert * @param[in] salen size of sa * * @returns new ZSockAddr instance **/ ZSockAddr * z_sockaddr_new(struct sockaddr *sa, gsize salen) { z_enter(); switch (sa->sa_family) { #ifndef G_OS_WIN32 case AF_INET6: if (salen >= sizeof(struct sockaddr_in6)) z_return(z_sockaddr_inet6_new2((struct sockaddr_in6 *) sa)); break; #endif case AF_INET: if (salen == sizeof(struct sockaddr_in)) z_return(z_sockaddr_inet_new2((struct sockaddr_in *) sa)); break; #ifndef G_OS_WIN32 case AF_UNIX: /* NOTE: the sockaddr_un structure might be less than struct sockaddr_un */ z_return(z_sockaddr_unix_new2((struct sockaddr_un *) sa, salen)); #endif default: /*LOG This message indicates an internal error, the program tried to use an unsupported address family. Please report this error to the Zorp QA team. */ z_log(NULL, CORE_ERROR, 3, "Unsupported socket family in z_sockaddr_new(); family='%d'", sa->sa_family); z_return(NULL); } z_return(NULL); }
/** * z_dispatch_connection: * @chain this * @conn The new connection to dispatch * * Iterates through the chain and dispatches the connection to the * chain items by passing it to their callbacks (for example to * z_py_zorp_dispatch_accept, which passes it to Dispatcher.accepted). */ static void z_dispatch_connection(ZDispatchChain *chain, ZConnection *conn) { GList *p; ZDispatchEntry *entry; gchar buf[256]; z_enter(); z_dispatch_chain_lock(chain); /* the list is ordered by priority */ for (p = chain->elements; p; p = g_list_next(p)) { entry = (ZDispatchEntry *) p->data; /*LOG This message reports that a new connections is coming. */ z_log(entry->session_id, CORE_DEBUG, 6, "Incoming connection; %s", conn ? z_connection_format(conn, buf, sizeof(buf)) : "conn=NULL"); if ((entry->callback)(conn, entry->callback_data)) { z_dispatch_chain_unlock(chain); z_return(); } } z_dispatch_chain_unlock(chain); /* nobody needed this connection, destroy it */ /*LOG This message indicates that a new connection was accepted, but no Listenet/Receiver/Proxy was interested in it. */ z_log(NULL, CORE_ERROR, 3, "Nobody was interested in this connection; %s", z_connection_format(conn, buf, sizeof(buf))); z_connection_destroy(conn, TRUE); z_return(); }
/** * Get the (absolute) filename assigned to the blob. * * @param[in] self this * @param[in] user Owner of the created file or NULL * @param[in] group Group of the created file or NULL * @param[in] mode Mode of the created file of -1 * @param[in] timeout timeout * * @returns The filename **/ const gchar * z_blob_get_file(ZBlob *self, const gchar *user, const gchar *group, gint mode, gint timeout) { const gchar *res = NULL; z_enter(); g_assert(self); if (!self->filename || !self->system) z_return(NULL); if (z_blob_lock(self, timeout)) { if (!self->is_in_file) { if (self->storage_locked) goto exit; g_mutex_lock(self->system->mtx_blobsys); /* swap_out() accesses the blob systems data directly, so it needs to be locked */ z_blob_swap_out(self); g_mutex_unlock(self->system->mtx_blobsys); } if (group || user) { uid_t user_id = -1; gid_t group_id = -1; if (user && !z_resolve_user(user, &user_id)) { z_log(NULL, CORE_ERROR, 3, "Cannot resolve user; user='******'", user); goto exit; } if (group && !z_resolve_group(group, &group_id)) { z_log(NULL, CORE_ERROR, 3, "Cannot resolve group; group='%s'", group); goto exit; } if (chown(self->filename, user_id, group_id) == -1) goto exit; } if ((mode != -1) && (chmod(self->filename, mode) == -1)) goto exit; res = self->filename; exit: if (res == NULL) z_blob_unlock(self); } z_return(res); }
gboolean z_kzorp_get_lookup_result(gint fd, struct z_kzorp_lookup_result *result) { socklen_t size = sizeof(*result); z_enter(); if (getsockopt(fd, SOL_IP, SO_KZORP_RESULT, result, &size) < 0) { z_log(NULL, CORE_ERROR, 3, "Error querying KZorp lookup result; fd='%d', error='%s'", fd, g_strerror(errno)); z_return(FALSE); } z_return(TRUE); }
/** * Create a new blob. * * @param[in] sys Blob system to create the blob into * @param[in] initial_size Initial size to allocate. * * This function creates a new blob. If sys is NULL, z_blob_system_default will be used. * * @returns The new blob instance **/ ZBlob* z_blob_new(ZBlobSystem *sys, gsize initial_size) { ZBlob *self; z_enter(); if (!sys) sys = z_blob_system_default; if (!sys || !sys->active) z_return(NULL); self = g_new0(ZBlob, 1); self->system = sys; self->filename = g_strdup_printf("%s/blob_XXXXXX", self->system->dir); self->fd = mkstemp(self->filename); if (self->fd < 0) { z_log(NULL, CORE_ERROR, 2, "Error creating blob file: file='%s', error='%s'", self->filename, strerror(errno)); g_free(self->filename); g_free(self); z_return(NULL); } z_refcount_set(&self->ref_cnt, 1); self->size = 0; self->alloc_size = 0; self->data = NULL; self->is_in_file = FALSE; self->mtx_reply = g_mutex_new(); self->cond_reply = g_cond_new(); self->mapped_ptr = NULL; self->mapped_length = 0; self->storage_locked = FALSE; z_blob_statistic_init(&self->stat); self->mtx_lock = g_mutex_new(); g_mutex_lock(self->system->mtx_blobsys); self->system->blobs = g_list_append(self->system->blobs, self); g_mutex_unlock(self->system->mtx_blobsys); if (initial_size > 0) z_blob_alloc(self, initial_size); z_return(self); }
/** * Increase reference count of a blob system. * * @param[in] self the blob system object **/ void z_blob_system_ref(ZBlobSystem *self) { z_enter(); z_refcount_inc(&self->ref_cnt); z_return(); }
/** * Increase reference count of blob and return a reference to it. * * @param[in] self this * * @returns self **/ ZBlob * z_blob_ref(ZBlob *self) { z_enter(); z_refcount_inc(&self->ref_cnt); z_return(self); }
/** * z_policy_call: * @handler: Python object whose method shall be called * @name: Method name * @args: Arguments to pass to the method * @called: Flag to store into whether the call succeeded or not (may be NULL) * @session_id: Session ID for logging * * If the requested method exists and is callable, calls it. * If @called is not NULL, it will be set if the call succeeded, cleared if not. * * Returns: * The return value of the call */ PyObject * z_policy_call(PyObject *handler, char *name, PyObject *args, gboolean *called, gchar *session_id) { PyObject *attr; PyObject *res; z_enter(); g_assert(PyThreadState_GET()); attr = PyObject_GetAttrString(handler, name); if (!attr || !PyCallable_Check(attr)) { if (attr) { Py_XDECREF(attr); PyErr_Format(PyExc_TypeError, "Event must be callable: %s", name); PyErr_Print(); /* produce a backtrace, and handle it immediately */ } PyErr_Clear(); Py_XDECREF(args); res = NULL; z_trace(NULL, "Cannot find function; name='%s'", name); if (called) *called = FALSE; } else { if (called) *called = TRUE; res = z_policy_call_object(attr, args, session_id); z_trace(NULL, "Function called; name='%s'", name); Py_XDECREF(attr); } z_return(res); }
/** * Create a new blob system using the given parameters. * * @param[in] dir directory to put the swapped blobs into * @param[in] dmax max disk usage size * @param[in] mmax max mem usage size * @param[in] low low water mark * @param[in] hiw high water mark * @param[in] nosw maximal size that wont't be swapped * * @returns The new blob system instance **/ ZBlobSystem* z_blob_system_new(const char *dir, gint64 dmax, gsize mmax, gsize low, gsize hiw, gsize nosw) { ZBlobSystem *self; z_enter(); self = g_new0(ZBlobSystem, 1); z_refcount_set(&self->ref_cnt, 1); self->dir = strdup(dir); self->disk_max = dmax; self->mem_max = mmax; self->disk_used = self->mem_used = 0; if (mmax <= low) low = mmax - 1; self->lowat = low; if (mmax <= hiw) hiw = mmax - 1; self->hiwat = hiw; self->noswap_max = nosw; self->blobs = NULL; self->mtx_blobsys = g_mutex_new(); self->cond_thread_started = g_cond_new(); self->req_queue = g_async_queue_new(); self->waiting_list = NULL; g_mutex_lock(self->mtx_blobsys); self->thr_management = g_thread_create((GThreadFunc)z_blob_system_threadproc, (gpointer)self, TRUE, &self->thread_error); g_cond_wait(self->cond_thread_started, self->mtx_blobsys); g_mutex_unlock(self->mtx_blobsys); self->active = TRUE; z_return(self); }
static guint z_plug_read_input(ZPlugSession *self, ZStream *input, ZPlugIOBuffer *buf) { GIOStatus rc; z_enter(); rc = z_stream_read(input, buf->buf, self->session_data->buffer_size, &buf->end, NULL); if (rc == G_IO_STATUS_NORMAL) { buf->packet_bytes += buf->end; buf->packet_count++; self->global_packet_count++; if (self->session_data->packet_stats_interval_packet && (self->global_packet_count % self->session_data->packet_stats_interval_packet) == 0) { if (!self->session_data->packet_stats(self, self->buffers[EP_CLIENT].packet_bytes, self->buffers[EP_CLIENT].packet_count, self->buffers[EP_SERVER].packet_bytes, self->buffers[EP_SERVER].packet_count, self->user_data)) { z_plug_update_eof_mask(self, EOF_ALL); rc = G_IO_STATUS_EOF; } } } z_return(rc); }
/** * Unlock a blob. * * @param self[in] this **/ void z_blob_unlock(ZBlob *self) { z_enter(); g_assert(self); g_mutex_unlock(self->mtx_lock); z_return(); }
/** * Write some data into the given position of the blob, expanding it if necessary. * * @param[in] self this * @param[in] pos position to write to * @param[in] data data to write * @param[in] req_datalen length of data * @param[in] timeout timeout * * @returns The amount of data written. **/ gsize z_blob_add_copy(ZBlob *self, gint64 pos, const gchar* data, gsize req_datalen, gint timeout) { off_t err; gssize written = 0; z_enter(); g_assert(self); g_assert(data); g_assert(pos >= 0); if (z_blob_lock(self, timeout)) { if (self->alloc_size < (pos + (gssize) req_datalen)) z_blob_alloc(self, pos + req_datalen); if (self->is_in_file) { gssize remain; err = lseek(self->fd, pos, SEEK_SET); if (err < 0) { z_log(NULL, CORE_ERROR, 0, "Blob error, lseek() failed; file='%s', error='%s'", self->filename, g_strerror(errno)); g_assert(0); } remain = req_datalen; while (remain > 0) { written = write(self->fd, data, remain); if (written < 0) { if (errno == EINTR) { continue; } else { z_log(NULL, CORE_ERROR, 0, "Blob error, write() failed; file='%s', error='%s'", self->filename, g_strerror(errno)); g_assert(0); } } remain -= written; } } else { memmove(self->data + pos, data, req_datalen); written = req_datalen; } if (self->size < (pos + written)) self->size = pos + written; self->stat.req_wr++; self->stat.total_wr += written; self->stat.last_accessed = time(NULL); z_blob_unlock(self); } z_return(written); }
/** * Decrease reference count of a blob system; destroy it if the count reaches zero. * * @param[in] self the blob system object * * This function decreases the reference count of the blob system * object given. If the reference count reaches zero, the blob system * will be destroyed. If there were pending requests in a to-be-destroyed * blob system, this fact will be logged. **/ void z_blob_system_unref(ZBlobSystem *self) { ZBlob *blob; GList *cur, *next; gint n; z_enter(); g_assert(self); if (z_refcount_dec(&self->ref_cnt)) { self->active = FALSE; /** @todo FIXME: itt lockolni kell */ g_async_queue_push(self->req_queue, Z_BLOB_THREAD_KILL); g_thread_join(self->thr_management); n = 0; for (cur = self->waiting_list; cur; cur = next) { next = cur->next; blob = (ZBlob*) cur->data; blob->approved = FALSE; z_blob_signal_ready(blob); self->waiting_list = g_list_delete_link(self->waiting_list, cur); n++; } if (n) z_log(NULL, CORE_INFO, 5, "Pending requests found for a to-be-destroyed blob system; num_requests='%d'", n); n = 0; for (cur = self->blobs; cur; cur = next) { next = cur->next; blob = (ZBlob*)cur->data; z_blob_unref(blob); n++; } if (n) z_log(NULL, CORE_INFO, 5, "Active blobs found in a to-be-destroyed blob system; num_blobs='%d'", n); if (self->dir) g_free(self->dir); if (g_mutex_trylock(self->mtx_blobsys)) { g_mutex_unlock(self->mtx_blobsys); g_mutex_free(self->mtx_blobsys); } else { /* Some blob operations are in progress: z_blob_new, _unref, _alloc, _get_file */ } g_cond_free(self->cond_thread_started); g_async_queue_unref(self->req_queue); g_list_free(self->waiting_list); g_free(self); } z_return(); }
/** * anypy_proxy_new: * @params: parameters for the AnyPyProxy class constructor * * This function is called upon startup to create a new AnyPy proxy. **/ ZProxy * anypy_proxy_new(ZProxyParams *params) { AnyPyProxy *self; z_enter(); self = Z_CAST(z_proxy_new(Z_CLASS(AnyPyProxy), params), AnyPyProxy); z_return(&self->super); }
static void z_header_set_destroy_foreach(gpointer key G_GNUC_UNUSED, gpointer value, gpointer user_data G_GNUC_UNUSED) { z_enter(); z_header_set_destroy_chain((GList *)value); z_return(); }
/** * Reads some data from the blob into a buffer. * * @param[in] self this * @param[in] pos position to read from * @param[in] data buffer to read into * @param[in] req_datalen bytes to read * @param[in] timeout timeout * * @returns The amount of data actually read. **/ gsize z_blob_get_copy(ZBlob *self, gint64 pos, gchar* data, gsize req_datalen, gint timeout) { off_t err; gssize rd = 0; z_enter(); g_assert(self); g_assert(data); g_assert(pos >= 0); if (pos < self->size) { if (req_datalen > (guint64) (self->size - pos)) req_datalen = self->size - pos; if (z_blob_lock(self, timeout)) { if (self->is_in_file) { gssize remain; err = lseek(self->fd, pos, SEEK_SET); if (err < 0) { z_log(NULL, CORE_ERROR, 0, "Blob error, lseek() failed; file='%s', error='%s'", self->filename, g_strerror(errno)); g_assert(0); } remain = req_datalen; while (remain > 0) { rd = read(self->fd, data, remain); if (rd < 0) { if (errno == EINTR) { continue; } else { z_log(NULL, CORE_ERROR, 0, "Blob error, read() failed; file='%s', error='%s'", self->filename, g_strerror(errno)); g_assert(0); } } remain -= rd; } } else { memmove(data, self->data + pos, req_datalen); rd = req_datalen; } self->stat.req_rd++; self->stat.total_rd += rd; self->stat.last_accessed = time(NULL); z_blob_unlock(self); } } z_return(rd); }
/** * finger_proxy_new: * @params: ZProxyParams structure * * Finger proxy constructor. Allocates and initializes a proxy instance, * starts proxy thread. **/ static ZProxy * finger_proxy_new(ZProxyParams *params) { FingerProxy *self; z_enter(); self = Z_CAST(z_proxy_new(Z_CLASS(FingerProxy), params), FingerProxy); z_return((ZProxy *) self); }
ZHeader * z_header_set_iterate(ZHeaderSet *self, gchar *key, gpointer *opaque) { ZHeader *res; GList *value = opaque ? (GList *) *opaque : NULL; z_enter(); if (value == NULL) value = (GList *) g_hash_table_lookup(self->headers, key); else value = g_list_next(value); if (value == NULL) z_return(NULL); res = value->data; if (opaque) *opaque = value; z_return(res); }
/** * Destroy the default blob system. **/ void z_blob_system_default_destroy(void) { z_enter(); if (z_blob_system_default) { z_blob_system_unref(z_blob_system_default); z_blob_system_default = NULL; } z_return(); }
GList * z_header_set_get_all_headers(ZHeaderSet *self) { GList *ret = NULL; z_enter(); g_hash_table_foreach(self->headers, z_header_set_append_foreach, &ret); ret = g_list_reverse(ret); z_return(ret); }
/** * Initialize the default blob system. **/ void z_blob_system_default_init(void) { z_enter(); z_blob_system_default = z_blob_system_new(z_blob_system_default_tmpdir, z_blob_system_default_max_disk_usage, z_blob_system_default_max_mem_usage, z_blob_system_default_lowat, z_blob_system_default_hiwat, z_blob_system_default_noswap_max); z_return(); }
/** * Release the lock on the blob after z_blob_get_file. * * @param[in] self this * * Besides releasing the lock itself, this function also updates the * blob size from the file size. **/ void z_blob_release_file(ZBlob *self) { struct stat st; z_enter(); g_assert(self); if (!fstat(self->fd, &st)) self->size = self->alloc_size = st.st_size; else z_log(NULL, CORE_ERROR, 3, "Cannot stat file on release, blob size may be incorrect from now;"); z_blob_unlock(self); z_return(); }
gboolean z_header_set_add(ZHeaderSet *self, GString *key, GString *value, gboolean multiple) { ZHeader *header; GList *hlist; z_enter(); header = g_new0(ZHeader, 1); header->key = key; header->value = value; hlist = g_hash_table_lookup(self->headers, header->key->str); if (!hlist || (header->key->str[0] == 'X') || multiple) { self->headers_count++; hlist = g_list_append(hlist, header); g_hash_table_insert(self->headers, header->key->str, hlist); z_return(TRUE); } z_return(FALSE); }
gboolean z_policy_tuple_get_verdict(ZPolicyObj *tuple, guint *verdict) { ZPolicyObj *tmp; z_enter(); if (!z_policy_seq_check(tuple)) { if (z_policy_var_parse(tuple, "i", verdict)) z_return(TRUE); /* not a sequence nor an int */ z_return(FALSE); } tmp = z_policy_seq_getitem(tuple, 0); if (!tmp || !z_policy_var_parse(tmp, "i", verdict)) { /* policy syntax error */ z_policy_var_unref(tmp); z_return(FALSE); } z_policy_var_unref(tmp); z_return(TRUE); }
/** * Truncates/expands a blob. * * @param[in] self this * @param[in] pos position to truncate at * @param[in] timeout timeout * * @returns TRUE on success **/ gboolean z_blob_truncate(ZBlob *self, gint64 pos, gint timeout) { gboolean res = FALSE; z_enter(); g_assert(self); g_assert(pos >= 0); if (z_blob_lock(self, timeout)) { z_blob_alloc(self, pos); z_blob_unlock(self); res = TRUE; } z_return(res); }
/** * telnet_copy_buf: * @to: * @from: * @bytes: * * * * Returns: * */ static gint telnet_copy_buf(ZIOBufferDyn *to, ZIOBuffer *from, guint bytes) { guint i; z_enter(); if ((i = to->size - to->end) < bytes) { /* we must allocate more buffer space */ to->size += (1 + bytes / TELNET_BUFFER_SIZE) * TELNET_BUFFER_SIZE; to->buf = g_realloc(to->buf, to->size); } for (i = 0; to->end < to->size && from->ofs < from->end && i < bytes; to->end++, from->ofs++, i++) to->buf[to->end] = from->buf[from->ofs]; z_return(i == bytes); }
static void z_header_set_destroy_chain(GList *list) { ZHeader *header; z_enter(); while (list) { header = (ZHeader *)list->data; g_string_free(header->key, TRUE); g_string_free(header->value, TRUE); g_free(header); list = g_list_delete_link(list, list); } z_return(); }
/** * Writes a blob out to disk, called only from z_blob_system_threadproc() * * @param[in] self this * * @warning Caller must hold a lock BOTH on the blob AND the blob system! **/ static void z_blob_swap_out(ZBlob *self) { off_t err; gssize written, remain; z_enter(); g_assert(self); if (!self->storage_locked && !self->is_in_file && self->system) { err = lseek(self->fd, 0, SEEK_SET); if (err < 0) { z_log(NULL, CORE_ERROR, 0, "Blob error, lseek() failed; file='%s', error='%s'", self->filename, g_strerror(errno)); g_assert(0); } remain = self->size; while (remain > 0) { written = write(self->fd, self->data, remain); if (written < 0) { if (errno == EINTR) { continue; } else { z_log(NULL, CORE_ERROR, 0, "Blob error, write() failed; file='%s', error='%s'", self->filename, g_strerror(errno)); g_assert(0); } } remain -= written; } self->is_in_file = 1; g_free(self->data); self->data = NULL; self->stat.swap_count++; self->stat.last_accessed = time(NULL); self->system->mem_used -= self->alloc_size; self->system->disk_used += self->alloc_size; } z_return(); }
/** * Lock a blob. * * @param[in] self this * @param[in] timeout Timeout for locking. A negative value means infinite and thus blocking mode. Zero means nonblocking mode. * * @returns TRUE if successfully locked. **/ gboolean z_blob_lock(ZBlob *self, gint timeout) { gboolean res; struct timeval tvnow, tvfinish; z_enter(); g_assert(self); if (timeout < 0) /* infinite timeout -> blocking mode */ { g_mutex_lock(self->mtx_lock); res = TRUE; } else if (timeout == 0) /* zero timeout -> nonblocking mode */ { res = g_mutex_trylock(self->mtx_lock); } else /* positive timeout */ { gettimeofday(&tvfinish, NULL); tvfinish.tv_sec += (timeout / 1000); tvfinish.tv_usec += 1000 * (timeout % 1000); tvfinish.tv_sec += (tvfinish.tv_usec / 1000000); tvfinish.tv_usec %= 1000000; /* FIXME: maybe g_cond_wait_timed_wait ? */ do { res = FALSE; if (g_mutex_trylock(self->mtx_lock)) { res = TRUE; break; } usleep(1000); gettimeofday(&tvnow, NULL); } while ((tvnow.tv_sec < tvfinish.tv_sec) || ((tvnow.tv_sec == tvfinish.tv_sec) && (tvnow.tv_usec < tvfinish.tv_usec))); } z_return(res); }
/** * Decrease reference count of blob; destroy it if the count reaches zero. * * @param[in] self this **/ void z_blob_unref(ZBlob *self) { z_enter(); if (self && z_refcount_dec(&self->ref_cnt)) { g_mutex_lock(self->system->mtx_blobsys); self->alloc_req = -self->alloc_size; self->system->blobs = g_list_remove(self->system->blobs, self); z_blob_check_alloc(self); g_mutex_unlock(self->system->mtx_blobsys); if (self->data) g_free(self->data); if (self->fd >= 0) close(self->fd); if (self->filename) { if (unlink(self->filename)) z_log(NULL, CORE_ERROR, 3, "Error removing blob file, unlink() failed; file='%s', error='%s'", self->filename, strerror(errno)); g_free(self->filename); self->filename = NULL; } g_mutex_free(self->mtx_reply); g_cond_free(self->cond_reply); if (g_mutex_trylock(self->mtx_lock)) { g_mutex_unlock(self->mtx_lock); g_mutex_free(self->mtx_lock); } else { z_log(NULL, CORE_ERROR, 3, "Error while destroying blob, someone still has a lock on it;"); /* someone has locked the blob by z_blob_get_file or _get_ptr, and forgot to release it */ } g_free(self); } z_return(); }