Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
Please login to access the resource
SUSE:SLE-15:Update
gvfs.11406
gvfs-nds.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File gvfs-nds.patch of Package gvfs.11406
Index: gvfs-1.34.0/daemon/gvfsbackendnds.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gvfs-1.34.0/daemon/gvfsbackendnds.c 2017-09-15 10:53:24.564258329 +0200 @@ -0,0 +1,747 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson <alexl@redhat.com> + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <glib/gstdio.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <dlfcn.h> +#include <sys/utsname.h> + +#include "gvfsbackendnds.h" +#include "gvfsjobmountmountable.h" +#include "gvfsjobopenforread.h" +#include "gvfsjobread.h" +#include "gvfsjobseekread.h" +#include "gvfsjobqueryinfo.h" +#include "gvfsjobenumerate.h" +#include "gvfsdaemonprotocol.h" +#include "gmounttracker.h" +#include "gvfsmonitor.h" + + + +typedef struct { + char *name; + char *name_normalized; + char *name_utf8; +} BrowseEntry; + +struct _GVfsBackendNds +{ + GVfsBackend parent_instance; + + char *server; + char *mounted_server; + + GMutex entries_lock; + GList *entries; + int entry_errno; +}; + +static GMountTracker *mount_tracker = NULL; + +G_DEFINE_TYPE (GVfsBackendNds, g_vfs_backend_nds, G_VFS_TYPE_BACKEND) + +static gboolean +is_root (const char *filename) +{ + const char *p; + + p = filename; + while (*p == '/') + p++; + + return *p == 0; +} + +static char * +normalize_nds_name_helper (const char *name, gssize len, gboolean valid_utf8) +{ + if (valid_utf8) + return g_utf8_casefold (name, len); + else + return g_ascii_strdown (name, len); +} + +static char * +normalize_nds_name (const char *name, gssize len) +{ + gboolean valid_utf8; + + valid_utf8 = g_utf8_validate (name, len, NULL); + return normalize_nds_name_helper (name, len, valid_utf8); +} + +static char * +nds_name_to_utf8 (const char *name, gboolean *valid_utf8_out) +{ + GString *string; + const gchar *remainder, *invalid; + gint remaining_bytes, valid_bytes; + gboolean valid_utf8; + + remainder = name; + remaining_bytes = strlen (name); + valid_utf8 = TRUE; + + string = g_string_sized_new (remaining_bytes); + while (remaining_bytes != 0) + { + if (g_utf8_validate (remainder, remaining_bytes, &invalid)) + break; + valid_utf8 = FALSE; + + valid_bytes = invalid - remainder; + + g_string_append_len (string, remainder, valid_bytes); + /* append U+FFFD REPLACEMENT CHARACTER */ + g_string_append (string, "\357\277\275"); + + remaining_bytes -= valid_bytes + 1; + remainder = invalid + 1; + } + + g_string_append (string, remainder); + + if (valid_utf8_out) + *valid_utf8_out = valid_utf8; + + return g_string_free (string, FALSE); +} + +static void +browse_entry_free (BrowseEntry *entry) +{ + if(entry->name != NULL) + g_free (entry->name); + if(entry != NULL) + g_free (entry); +} + + +static void +g_vfs_backend_nds_finalize (GObject *object) +{ + GVfsBackendNds *backend; + + backend = G_VFS_BACKEND_NDS (object); + + if(backend->mounted_server != NULL) + g_free (backend->mounted_server); + if(backend->server != NULL) + g_free (backend->server); + + g_mutex_clear (&backend->entries_lock); + + g_list_foreach (backend->entries, (GFunc)browse_entry_free, NULL); + if(backend->entries != NULL) + g_list_free (backend->entries); + + if (G_OBJECT_CLASS (g_vfs_backend_nds_parent_class)->finalize) + (*G_OBJECT_CLASS (g_vfs_backend_nds_parent_class)->finalize) (object); +} + +static void +g_vfs_backend_nds_init (GVfsBackendNds *backend) +{ + g_mutex_init(&backend->entries_lock); + + if (mount_tracker == NULL) + mount_tracker = g_mount_tracker_new (NULL, FALSE); +} + + +static void +update_cache (GVfsBackendNds *backend,const char *filename) +{ + GList *entries; + int entry_errno; + char *objectname=NULL; + char *treename=NULL; + GList *objectlist=NULL; + GList *object=NULL; + FILE *fptr=NULL; + void *handle=NULL; //handle for dlopen + int res; + char *ptr=NULL; + char *server_name=NULL; + struct utsname utsbuf; +/* Function Pointer to /opt/novell/lib/libinterface.so */ + int (*retrieve_children)(char *,char ***,int *); + char **object_list_array=NULL; + int num_objects=0; + int i; + + entries = NULL; + entry_errno = 0; + + if(backend->server != NULL) + { + server_name = g_strdup(backend->server); + if((ptr = strchr(server_name,'%')) != NULL) + *ptr = '\0'; + + } + + if(backend->server == NULL) //retrieve Tree names + { + treename = (char *)malloc(sizeof(char) * 80); + system("/opt/novell/ncl/bin/retrieve_trees.pl"); + fptr = fopen("/tmp/ndstrees.txt","r"); + if(fptr == NULL) + { + entry_errno = -1; + goto out; + } + while (fgets(treename,80,fptr) != NULL) + { + objectlist = g_list_append(objectlist,g_strdup(treename)); + memset(treename,'\0',80); + } + fclose(fptr); + free(treename); + } + + else //retrieve object names + { + uname(&utsbuf); + if(!strcmp(utsbuf.machine,"x86_64")) + handle = dlopen("/usr/lib64/libinterface.so",RTLD_NOW | RTLD_DEEPBIND); + else + handle = dlopen("/usr/lib/libinterface.so",RTLD_NOW | RTLD_DEEPBIND); + + if(handle == NULL) + { + return; + } + *(void **)(&retrieve_children) = dlsym(handle,"retrieve_children"); + + if(retrieve_children != NULL) + { + res = (*retrieve_children)(server_name,&object_list_array,&num_objects); + if(res == 0) + { + for(i=0;i < num_objects; i++) + { + objectlist = g_list_append(objectlist,g_strdup(*(object_list_array+ i))); + } + } + } + else + { + return; + } + dlclose(handle); + } + + + gboolean valid_utf8; + BrowseEntry *entry; + + for(object = objectlist;object != NULL;object = object->next) + { + entry = g_new (BrowseEntry, 1); + objectname = object->data; + entry->name = g_strdup (objectname); + entry->name_utf8 = nds_name_to_utf8 (objectname, &valid_utf8); + entry->name_normalized = normalize_nds_name_helper (objectname, -1, valid_utf8); + + entries = g_list_append (entries, entry); + } + + //if(objectlist != NULL) + //g_list_free(objectlist); + +out: + + if(entry_errno == 0) + { + g_mutex_lock (&backend->entries_lock); + backend->entries = entries; + g_mutex_unlock (&backend->entries_lock); + } + backend->entry_errno = entry_errno; + +} + +static BrowseEntry * +find_entry_unlocked (GVfsBackendNds *backend, + const char *filename) +{ + BrowseEntry *entry, *found; + GList *l; + char *end; + int len; + + while (*filename == '/') + filename++; + + end = strchr (filename, '/'); + if (end) + { + len = end - filename; + + while (*end == '/') + end++; + + if (*end != 0) + return NULL; + } + else + len = strlen (filename); + + /* First look for an exact filename match */ + found = NULL; + for (l = backend->entries; l != NULL; l = l->next) + { + entry = l->data; + + if (strncmp (filename, entry->name, len) == 0 && + strlen (entry->name) == len) + { + found = entry; + break; + } + } + + if (found == NULL) + { + char *normalized; + /* That failed, try normalizing the filename */ + normalized = normalize_nds_name (filename, len); + + for (l = backend->entries; l != NULL; l = l->next) + { + entry = l->data; + + if (strcmp (normalized, entry->name_normalized) == 0) + { + found = entry; + break; + } + } + if(normalized != NULL) + g_free (normalized); + } + + return found; +} + +static GMountSpec * +get_mount_spec_for_share (const char *server, const char *share) +{ + GMountSpec *mount_spec; + char *normalized; + + mount_spec = g_mount_spec_new ("nds"); + g_mount_spec_set (mount_spec, "host", server); + g_mount_spec_set (mount_spec, "share", share); + + return mount_spec; +} + + + +static void +do_mount (GVfsBackend *backend, + GVfsJobMount *job, + GMountSpec *mount_spec, + GMountSource *mount_source, + gboolean is_automount) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + char *display_name; + char *icon; + GMountSpec *browse_mount_spec; + + + icon = NULL; + if (op_backend->server == NULL) + { + display_name = g_strdup (_("NVVFS Top Directory")); + browse_mount_spec = g_mount_spec_new ("nds"); + icon = "network-workgroup"; + } + else + { + display_name = g_strdup_printf (_("NVVFS Objects")); + browse_mount_spec = g_mount_spec_new ("nds"); + //g_mount_spec_set (browse_mount_spec, "host", op_backend->mounted_server); + g_mount_spec_set (browse_mount_spec, "host", op_backend->server); + icon = "network-server"; + } + + g_vfs_backend_set_display_name (backend, display_name); + g_free (display_name); + if (icon) + g_vfs_backend_set_icon_name (backend, icon); + g_vfs_backend_set_user_visible (backend, FALSE); + g_vfs_backend_set_mount_spec (backend, browse_mount_spec); + g_mount_spec_unref (browse_mount_spec); + + g_vfs_job_succeeded (G_VFS_JOB (job)); +} + +static gboolean +try_mount (GVfsBackend *backend, + GVfsJobMount *job, + GMountSpec *mount_spec, + GMountSource *mount_source, + gboolean is_automount) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + const char *server; + + server = g_mount_spec_get (mount_spec, "host"); + if (server) + { + op_backend->server = g_strdup (server); + op_backend->mounted_server = g_strdup (server); + } + else + { + op_backend->server = NULL; + op_backend->mounted_server = NULL; + } + + return FALSE; +} + +static void +run_mount_mountable (GVfsBackendNds *backend, + GVfsJobMountMountable *job, + const char *filename, + GMountSource *mount_source) +{ + GMountSpec *mount_spec; + + g_mutex_lock (&backend->entries_lock); + + backend->server = g_strdup(filename); + + mount_spec = get_mount_spec_for_share (backend->server, filename); + g_vfs_job_mount_mountable_set_target (job, mount_spec, "/", TRUE); + g_mount_spec_unref (mount_spec); + g_mutex_unlock (&backend->entries_lock); + + g_vfs_job_succeeded (G_VFS_JOB (job)); +} + + +static gboolean +do_mount_mountable (GVfsBackend *backend, + GVfsJobMountMountable *job, + const char *filename, + GMountSource *mount_source) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + + update_cache (op_backend,filename); + + run_mount_mountable (op_backend, + job, + filename, + mount_source); + return TRUE; +} + +static gboolean +try_mount_mountable (GVfsBackend *backend, + GVfsJobMountMountable *job, + const char *filename, + GMountSource *mount_source) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + + + if (is_root (filename)) + { + g_vfs_job_failed (G_VFS_JOB (job), + G_IO_ERROR, G_IO_ERROR_NOT_MOUNTABLE_FILE, + _("The file is not a mountable")); + return TRUE; + } + +update_cache (op_backend,filename); + + run_mount_mountable (op_backend, + job, + filename, + mount_source); + return TRUE; +} + + + +#define SUB_DELIM_CHARS "!$&'()*+,;=" + +static gboolean +is_valid (char c, const char *reserved_chars_allowed) +{ + if (g_ascii_isalnum (c) || + c == '-' || + c == '.' || + c == '_' || + c == '~') + return TRUE; + + if (reserved_chars_allowed && + strchr (reserved_chars_allowed, c) != NULL) + return TRUE; + + return FALSE; +} + +static void +g_string_append_encoded (GString *string, + const char *encoded, + const char *encoded_end, + const char *reserved_chars_allowed) +{ + char c; + static const gchar hex[16] = "0123456789ABCDEF"; + + if (encoded_end == NULL) + encoded_end = encoded + strlen (encoded); + + while (encoded < encoded_end) + { + c = *encoded++; + + if (is_valid (c, reserved_chars_allowed)) + g_string_append_c (string, c); + else + { + g_string_append_c (string, '%'); + g_string_append_c (string, hex[((guchar)c) >> 4]); + g_string_append_c (string, hex[((guchar)c) & 0xf]); + } + } +} + +static void +get_file_info_from_entry (GVfsBackendNds *backend, BrowseEntry *entry, GFileInfo *info) +{ + GString *uri; + GIcon *icon; + + g_file_info_set_name (info, entry->name); + g_file_info_set_display_name (info, entry->name_utf8); + g_file_info_set_edit_name (info, entry->name_utf8); + g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL, TRUE); + + if(backend->server == NULL) //NDS-Tree icon + icon = g_themed_icon_new ("ncl-ndstree"); + else + icon = g_themed_icon_new ("ncl-nwcontext"); + + if (icon) + { + g_file_info_set_icon (info, icon); + g_object_unref (icon); + } + + g_file_info_set_file_type (info, G_FILE_TYPE_SHORTCUT); + + uri = g_string_new ("nds://"); + g_string_append_encoded (uri, entry->name, NULL, NULL); + g_string_append_c (uri, '/'); + + g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, uri->str); + + g_string_free (uri, TRUE); + +} + +static void +run_query_info (GVfsBackendNds *backend, + GVfsJobQueryInfo *job, + const char *filename, + GFileInfo *info, + GFileAttributeMatcher *matcher) +{ + BrowseEntry *entry; + + g_mutex_lock (&backend->entries_lock); + + entry = find_entry_unlocked (backend, filename); + + if (entry) + get_file_info_from_entry (backend, entry, info); + + g_mutex_unlock (&backend->entries_lock); + + if (entry) + g_vfs_job_succeeded (G_VFS_JOB (job)); + else + g_vfs_job_failed (G_VFS_JOB (job), + G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _("File doesn't exist")); +} + +static void +do_query_info (GVfsBackend *backend, + GVfsJobQueryInfo *job, + const char *filename, + GFileQueryInfoFlags flags, + GFileInfo *info, + GFileAttributeMatcher *matcher) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + + run_query_info (op_backend, job, filename, info, matcher); +} + +static gboolean +try_query_info (GVfsBackend *backend, + GVfsJobQueryInfo *job, + const char *filename, + GFileQueryInfoFlags flags, + GFileInfo *info, + GFileAttributeMatcher *matcher) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + const char *icon_name = NULL; + GIcon *icon; + + if (filename && is_root (filename)) + { + g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY); + g_file_info_set_name (info, "/"); + g_file_info_set_display_name (info, g_vfs_backend_get_display_name (backend)); + /* FIXME: This API does not seem to be in gvfs-1.2.0. Find a suitable replacement? */ + /* icon_name = g_vfs_backend_get_icon_name (backend); */ + if (icon_name) + { + icon = g_themed_icon_new (icon_name); + g_file_info_set_icon (info, icon); + g_object_unref (icon); + } + g_vfs_job_succeeded (G_VFS_JOB (job)); + + return TRUE; + } + + return FALSE; + + run_query_info (op_backend, job, filename, info, matcher); + return TRUE; +} + +static void +run_enumerate (GVfsBackendNds *backend, + GVfsJobEnumerate *job, + const char *filename, + GFileAttributeMatcher *matcher) +{ + GList *files, *l; + GFileInfo *info; + + g_vfs_job_succeeded (G_VFS_JOB (job)); + + files = NULL; + g_mutex_lock (&backend->entries_lock); + for (l = backend->entries; l != NULL; l = l->next) + { + BrowseEntry *entry = l->data; + + info = g_file_info_new (); + get_file_info_from_entry (backend, entry, info); + + files = g_list_prepend (files, info); + } + g_mutex_unlock (&backend->entries_lock); + + files = g_list_reverse (files); + + g_vfs_job_enumerate_add_infos (job, files); + g_list_foreach (files, (GFunc)g_object_unref, NULL); + g_list_free (files); + + g_vfs_job_enumerate_done (job); +} + +static void +do_enumerate (GVfsBackend *backend, + GVfsJobEnumerate *job, + const char *filename, + GFileAttributeMatcher *matcher, + GFileQueryInfoFlags flags) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + + update_cache (op_backend,filename); + + run_enumerate (op_backend, job, filename, matcher); +} + +static gboolean +try_enumerate (GVfsBackend *backend, + GVfsJobEnumerate *job, + const char *filename, + GFileAttributeMatcher *matcher, + GFileQueryInfoFlags flags) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + + update_cache (op_backend,filename); + + run_enumerate (op_backend, job, filename, matcher); + return TRUE; +} + + +static void +g_vfs_backend_nds_class_init (GVfsBackendNdsClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass); + + gobject_class->finalize = g_vfs_backend_nds_finalize; + + backend_class->mount = do_mount; + backend_class->try_mount = try_mount; + backend_class->query_info = do_query_info; + backend_class->try_query_info = try_query_info; + backend_class->enumerate = do_enumerate; + backend_class->try_enumerate = try_enumerate; + backend_class->try_mount_mountable = try_mount_mountable; + backend_class->mount_mountable = do_mount_mountable; +} + +void +g_vfs_nds_daemon_init (void) +{ + g_set_application_name (_("Displaying Embedded Objects")); +} Index: gvfs-1.34.0/daemon/gvfsbackendnds.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gvfs-1.34.0/daemon/gvfsbackendnds.h 2017-09-15 10:53:56.159989373 +0200 @@ -0,0 +1,53 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson <alexl@redhat.com> + */ + +#ifndef __G_VFS_BACKEND_NDS_H__ +#define __G_VFS_BACKEND_NDS_H__ + +#include <gvfsbackend.h> +#include <gmountspec.h> + +G_BEGIN_DECLS + +#define G_VFS_TYPE_BACKEND_NDS (g_vfs_backend_nds_get_type ()) +#define G_VFS_BACKEND_NDS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_BACKEND_NDS, GVfsBackendNds)) +#define G_VFS_BACKEND_NDS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_BACKEND_NDS, GVfsBackendNdsClass)) +#define G_VFS_IS_BACKEND_NDS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_BACKEND_NDS)) +#define G_VFS_IS_BACKEND_NDS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_BACKEND_NDS)) +#define G_VFS_BACKEND_NDS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_BACKEND_NDS, GVfsBackendNdsClass)) + +typedef struct _GVfsBackendNds GVfsBackendNds; +typedef struct _GVfsBackendNdsClass GVfsBackendNdsClass; + +struct _GVfsBackendNdsClass +{ + GVfsBackendClass parent_class; +}; + +GType g_vfs_backend_nds_get_type (void) G_GNUC_CONST; + +#define BACKEND_SETUP_FUNC g_vfs_nds_daemon_init +void g_vfs_nds_daemon_init (void); + +G_END_DECLS + +#endif /* __G_VFS_BACKEND_NDS_H__ */ Index: gvfs-1.34.0/daemon/Makefile.am =================================================================== --- gvfs-1.34.0.orig/daemon/Makefile.am 2017-09-15 10:15:10.715090208 +0200 +++ gvfs-1.34.0/daemon/Makefile.am 2017-09-15 10:15:46.554940823 +0200 @@ -144,6 +144,10 @@ mount_DATA += nvvfs.mount libexec_PROGRAMS += gvfsd-nvvfs +mount_in_files += nds.mount.in +mount_DATA += nds.mount +libexec_PROGRAMS += gvfsd-nds + EXTRA_DIST = \ $(service_in_files) \ $(systemd_user_in_files) \ @@ -528,6 +532,19 @@ gvfsd_http_LDADD = $(libraries) $(HTTP_LIBS) +gvfsd_nds_SOURCES = \ + gvfsbackendnds.c gvfsbackendnds.h \ + daemon-main.c daemon-main.h \ + daemon-main-generic.c + +gvfsd_nds_CPPFLAGS = $(flags) \ + -DBACKEND_HEADER=gvfsbackendnds.h \ + -DDEFAULT_BACKEND_TYPE=nds \ + -DMAX_JOB_THREADS=1 \ + -DBACKEND_TYPES='"nds", G_VFS_TYPE_BACKEND_NDS,' + +gvfsd_nds_LDADD = $(libraries) -ldl + gvfsd_nvvfs_SOURCES = \ gvfsbackendnvvfs.c gvfsbackendnvvfs.h \ daemon-main.c daemon-main.h \ Index: gvfs-1.34.0/daemon/nds.mount.in =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gvfs-1.34.0/daemon/nds.mount.in 2017-09-15 10:15:46.554940823 +0200 @@ -0,0 +1,5 @@ +[Mount] +Type=nds +Exec=@libexecdir@/gvfsd-nds +AutoMount=false +Scheme=nds
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor