nfs-ganesha 1.4

nfs_rpc_callback_simulator.c

Go to the documentation of this file.
00001 /*
00002  * vim:expandtab:shiftwidth=8:tabstop=8:
00003  *
00004  * Copyright (C) 2010, The Linux Box Corporation
00005  * Contributor : Matt Benjamin <matt@linuxbox.com>
00006  *
00007  * Some portions Copyright CEA/DAM/DIF  (2008)
00008  * contributeur : Philippe DENIEL   philippe.deniel@cea.fr
00009  *                Thomas LEIBOVICI  thomas.leibovici@cea.fr
00010  *
00011  *
00012  * This program is free software; you can redistribute it and/or
00013  * modify it under the terms of the GNU Lesser General Public
00014  * License as published by the Free Software Foundation; either
00015  * version 3 of the License, or (at your option) any later version.
00016  *
00017  * This program is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00020  * Lesser General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU Lesser General Public
00023  * License along with this library; if not, write to the Free Software
00024  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00025  *
00026  * -------------
00027  */
00028 
00029 #ifdef HAVE_CONFIG_H
00030 #include "config.h"
00031 #endif
00032 
00033 #ifdef _SOLARIS
00034 #include "solaris_port.h"
00035 #endif                          /* _SOLARIS */
00036 
00037 #include <unistd.h>
00038 #include <sys/types.h>
00039 #include <sys/param.h>
00040 #include <time.h>
00041 #include <pthread.h>
00042 #include <assert.h>
00043 #include "nlm_list.h"
00044 #include "abstract_mem.h"
00045 #include "fsal.h"
00046 #include "nfs_core.h"
00047 #include "log.h"
00048 #include "nfs_rpc_callback.h"
00049 #include "nfs_rpc_callback_simulator.h"
00050 #include "sal_functions.h"
00051 #include "ganesha_dbus.h"
00052 
00070 /* XML data to answer org.freedesktop.DBus.Introspectable.Introspect requests */
00071 static const char* introspection_xml =
00072 "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
00073 "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
00074 "<node>\n"
00075 "  <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
00076 "    <method name=\"Introspect\">\n"
00077 "      <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
00078 "    </method>\n"
00079 "  </interface>\n"
00080 "  <interface name=\"org.ganesha.nfsd.cbsim\">\n"
00081 "    <method name=\"get_client_ids\">\n"
00082 "      <arg name=\"clientids\" direction=\"out\" type=\"at\"/>\n"
00083 "    </method>\n"
00084 "    <method name=\"fake_recall\">\n"
00085 "      <arg name=\"clientid\" direction=\"in\" type=\"t\"/>\n"
00086 "    </method>\n"
00087 "  </interface>\n"
00088 "</node>\n"
00089 ;
00090 
00091 extern hash_table_t *ht_confirmed_client_id;
00092 
00093 static DBusHandlerResult
00094 nfs_rpc_cbsim_get_client_ids(DBusConnection *conn, DBusMessage *msg,
00095                       void *user_data)
00096 {
00097   DBusMessage* reply;
00098   static uint32_t i, serial = 1;
00099   hash_table_t *ht = ht_confirmed_client_id;
00100   struct rbt_head *head_rbt;
00101   hash_data_t *pdata = NULL;
00102   struct rbt_node *pn;
00103   nfs_client_id_t *pclientid;
00104   uint64_t clientid;
00105   DBusMessageIter iter, sub_iter;
00106 
00107   /* create a reply from the message */
00108   reply = dbus_message_new_method_return(msg);
00109   dbus_message_iter_init_append(reply, &iter);
00110 
00111   dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
00112                                    DBUS_TYPE_UINT64_AS_STRING, &sub_iter);
00113   /* For each bucket of the hashtable */
00114   for(i = 0; i < ht->parameter.index_size; i++) {
00115     head_rbt = &(ht->partitions[i].rbt);
00116     
00117     /* acquire mutex */
00118     pthread_rwlock_wrlock(&(ht->partitions[i].lock));
00119     
00120     /* go through all entries in the red-black-tree*/
00121     RBT_LOOP(head_rbt, pn) {
00122       pdata = RBT_OPAQ(pn);
00123       pclientid =
00124         (nfs_client_id_t *)pdata->buffval.pdata;
00125       clientid = pclientid->cid_clientid;
00126       dbus_message_iter_append_basic(&sub_iter, DBUS_TYPE_UINT64, &clientid);
00127       RBT_INCREMENT(pn);      
00128     }
00129     pthread_rwlock_unlock(&(ht->partitions[i].lock));
00130   }
00131   dbus_message_iter_close_container(&iter, &sub_iter);
00132   /* send the reply && flush the connection */
00133   if (!dbus_connection_send(conn, reply, &serial)) {
00134       LogCrit(COMPONENT_DBUS, "reply failed");
00135   }
00136   dbus_connection_flush(conn);
00137   dbus_message_unref(reply);
00138   serial++;
00139   return (DBUS_HANDLER_RESULT_HANDLED);
00140 }
00141 
00142 static int32_t
00143 cbsim_test_bchan(clientid4 clientid)
00144 {
00145     int32_t tries, code = 0;
00146     nfs_client_id_t *pclientid = NULL;
00147     struct timeval CB_TIMEOUT = {15, 0};
00148     rpc_call_channel_t *chan;
00149     enum clnt_stat stat;
00150 
00151     code = nfs_client_id_get_confirmed(clientid, &pclientid);
00152     if (code != CLIENT_ID_SUCCESS) {
00153         LogCrit(COMPONENT_NFS_CB,
00154                 "No clid record for %"PRIx64" (%d) code %d", clientid,
00155                 (int32_t) clientid, code);
00156         code = EINVAL;
00157         goto out;
00158     }
00159 
00160     assert(pclientid);
00161 
00162     /* create (fix?) channel */
00163     for (tries = 0; tries < 2; ++tries) {
00164 
00165         chan = nfs_rpc_get_chan(pclientid, NFS_RPC_FLAG_NONE);
00166         if (! chan) {
00167             LogCrit(COMPONENT_NFS_CB, "nfs_rpc_get_chan failed");
00168             goto out;
00169         }
00170 
00171         if (! chan->clnt) {
00172             LogCrit(COMPONENT_NFS_CB, "nfs_rpc_get_chan failed (no clnt)");
00173             goto out;
00174         }
00175 
00176         /* try the CB_NULL proc -- inline here, should be ok-ish */
00177         stat = rpc_cb_null(chan, CB_TIMEOUT);
00178         LogDebug(COMPONENT_NFS_CB,
00179                  "rpc_cb_null on client %"PRIx64" returns %d",
00180                  clientid, stat);
00181 
00182 
00183         /* RPC_INTR indicates that we should refresh the channel
00184          * and retry */
00185         if (stat != RPC_INTR)
00186             break;
00187     }
00188 
00189 out:
00190     return (code);
00191 }
00192 
00193 /*
00194  * Demonstration callback invocation.
00195  */
00196 static void cbsim_free_compound(nfs4_compound_t *cbt)
00197 {
00198     int ix;
00199     nfs_cb_argop4 *argop = NULL;
00200 
00201     for (ix = 0; ix < cbt->v_u.v4.args.argarray.argarray_len; ++ix) {
00202         argop = cbt->v_u.v4.args.argarray.argarray_val + ix;
00203         if (argop) {
00204             switch (argop->argop) {
00205             case  NFS4_OP_CB_RECALL:
00206                 gsh_free(argop->nfs_cb_argop4_u.opcbrecall.fh.nfs_fh4_val);
00207                 break;
00208             default:
00209                 /* TODO:  ahem */
00210                 break;
00211             }
00212         }
00213     }
00214 
00215     /* XXX general free (move ?) */
00216     cb_compound_free(cbt);
00217 }
00218 
00219 static int32_t cbsim_completion_func(rpc_call_t* call, rpc_call_hook hook,
00220                                      void* arg, uint32_t flags)
00221 {
00222     LogDebug(COMPONENT_NFS_CB, "%p %s", call,
00223              (hook == RPC_CALL_ABORT) ?
00224              "RPC_CALL_ABORT" :
00225              "RPC_CALL_COMPLETE");
00226     switch (hook) {
00227     case RPC_CALL_COMPLETE:
00228         /* potentially, do something more interesting here */
00229         LogDebug(COMPONENT_NFS_CB, "call result: %d", call->stat);
00230         free_rpc_call(call);
00231         break;
00232     default:
00233         LogDebug(COMPONENT_NFS_CB, "%p unknown hook %d", call, hook);
00234         break;
00235     }
00236 
00237     return (0);
00238 }
00239 
00240 static int32_t
00241 cbsim_fake_cbrecall(clientid4 clientid)
00242 {
00243     int32_t code = 0;
00244     nfs_client_id_t *pclientid = NULL;
00245     rpc_call_channel_t *chan = NULL;
00246     nfs_cb_argop4 argop[1];
00247     rpc_call_t *call;
00248 
00249     LogDebug(COMPONENT_NFS_CB,
00250              "called with clientid %"PRIx64, clientid);
00251 
00252     code  = nfs_client_id_get_confirmed(clientid, &pclientid);
00253     if (code != CLIENT_ID_SUCCESS) {
00254         LogCrit(COMPONENT_NFS_CB,
00255                 "No clid record for %"PRIx64" (%d) code %d", clientid,
00256                 (int32_t) clientid, code);
00257         code = EINVAL;
00258         goto out;
00259     }
00260 
00261     assert(pclientid);
00262 
00263     chan = nfs_rpc_get_chan(pclientid, NFS_RPC_FLAG_NONE);
00264     if (! chan) {
00265         LogCrit(COMPONENT_NFS_CB, "nfs_rpc_get_chan failed");
00266         goto out;
00267     }
00268 
00269     if (! chan->clnt) {
00270         LogCrit(COMPONENT_NFS_CB, "nfs_rpc_get_chan failed (no clnt)");
00271         goto out;
00272     }
00273 
00274     /* allocate a new call--freed in completion hook */
00275     call = alloc_rpc_call();
00276     call->chan = chan;
00277 
00278     /* setup a compound */
00279     cb_compound_init_v4(&call->cbt, 6,
00280                         pclientid->cid_cb.cb_u.v40.cb_callback_ident,
00281                         "brrring!!!", 10);
00282 
00283     /* TODO: api-ify */
00284     memset(argop, 0, sizeof(nfs_cb_argop4));
00285     argop->argop = NFS4_OP_CB_RECALL;
00286     argop->nfs_cb_argop4_u.opcbrecall.stateid.seqid = 0xdeadbeef;
00287     strlcpy(argop->nfs_cb_argop4_u.opcbrecall.stateid.other,
00288             "0xdeadbeef", 12);
00289     argop->nfs_cb_argop4_u.opcbrecall.truncate = TRUE;
00290     argop->nfs_cb_argop4_u.opcbrecall.fh.nfs_fh4_len = 11;
00291     /* leaks, sorry */
00292     argop->nfs_cb_argop4_u.opcbrecall.fh.nfs_fh4_val = gsh_strdup("0xabadcafe");
00293 
00294     /* add ops, till finished (dont exceed count) */
00295     cb_compound_add_op(&call->cbt, argop);
00296 
00297     /* set completion hook */
00298     call->call_hook = cbsim_completion_func;
00299 
00300     /* call it (here, in current thread context) */
00301     code = nfs_rpc_submit_call(call,
00302                                NFS_RPC_FLAG_NONE /* NFS_RPC_CALL_INLINE */);
00303 
00304 out:
00305     return (code);
00306 }
00307 
00308 static DBusHandlerResult
00309 nfs_rpc_cbsim_fake_recall(DBusConnection *conn, DBusMessage *msg,
00310                          void *user_data)
00311 {
00312    DBusMessage* reply;
00313    DBusMessageIter args;
00314    static uint32_t serial = 1;
00315    clientid4 clientid = 9315; /* XXX ew! */
00316 
00317    LogDebug(COMPONENT_NFS_CB, "called!");
00318 
00319    /* read the arguments */
00320    if (!dbus_message_iter_init(msg, &args))
00321        LogDebug(COMPONENT_DBUS, "message has no arguments"); 
00322    else if (DBUS_TYPE_UINT64 != 
00323             dbus_message_iter_get_arg_type(&args)) 
00324        LogDebug(COMPONENT_DBUS, "arg not uint64"); 
00325    else {
00326        dbus_message_iter_get_basic(&args, &clientid);
00327        LogDebug(COMPONENT_DBUS, "param: %"PRIx64, clientid);
00328    }
00329 
00330    (void) cbsim_test_bchan(clientid);
00331    (void) cbsim_fake_cbrecall(clientid);
00332 
00333    reply = dbus_message_new_method_return(msg);
00334    if (!dbus_connection_send(conn, reply, &serial)) {
00335        LogCrit(COMPONENT_DBUS, "reply failed"); 
00336    }
00337 
00338    dbus_connection_flush(conn);
00339    dbus_message_unref(reply);
00340    serial++;
00341 
00342    return (DBUS_HANDLER_RESULT_HANDLED);
00343 }
00344 
00345 static DBusHandlerResult
00346 nfs_rpc_cbsim_introspection(DBusConnection *conn, DBusMessage *msg,
00347                          void *user_data)
00348 {
00349   static uint32_t serial = 1;
00350   DBusMessage* reply;
00351   DBusMessageIter iter;
00352   
00353   /* create a reply from the message */ 
00354   reply = dbus_message_new_method_return(msg);
00355   dbus_message_iter_init_append(reply, &iter);
00356   dbus_message_iter_append_basic( &iter, DBUS_TYPE_STRING,
00357                                   &introspection_xml );
00358 
00359   /* send the reply && flush the connection */
00360   if (! dbus_connection_send(conn, reply, &serial)) {
00361       LogCrit(COMPONENT_DBUS, "reply failed");
00362   }
00363 
00364   dbus_connection_flush(conn);
00365   dbus_message_unref(reply);
00366   serial++;
00367 
00368   return (DBUS_HANDLER_RESULT_HANDLED);
00369 }
00370 
00371 static DBusHandlerResult
00372 nfs_rpc_cbsim_entrypoint(DBusConnection *conn, DBusMessage *msg,
00373                              void *user_data)
00374 {
00375     const char *interface = dbus_message_get_interface(msg);
00376     const char *method = dbus_message_get_member(msg);
00377 
00378     if ((interface && (! strcmp(interface, DBUS_INTERFACE_INTROSPECTABLE))) ||
00379         (method && (! strcmp(method, "Introspect")))) {
00380         return(nfs_rpc_cbsim_introspection(conn, msg, user_data));
00381     }
00382 
00383     if (method) {
00384         if (! strcmp(method, "get_client_ids"))
00385             return(nfs_rpc_cbsim_get_client_ids(conn, msg, user_data));
00386 
00387         if (! strcmp(method, "fake_recall"))
00388             return(nfs_rpc_cbsim_fake_recall(conn, msg, user_data));
00389     }
00390 
00391     return (DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
00392 }
00393 
00394 /*
00395  * Initialize subsystem
00396  */
00397 void nfs_rpc_cbsim_pkginit(void)
00398 {
00399     (void) gsh_dbus_register_path("CBSIM", nfs_rpc_cbsim_entrypoint);
00400     LogEvent(COMPONENT_NFS_CB, "Callback Simulator Initialized");
00401 }
00402 
00403 /*
00404  * Shutdown subsystem
00405  */
00406 void nfs_rpc_cbsim_pkgshutdown(void)
00407 {
00408 
00409 }