diff --git a/src/create.c b/src/create.c
index b0365a2f..b5f93fe1 100644
--- a/src/create.c
+++ b/src/create.c
@@ -277,7 +277,7 @@ do_link(int descr, dbref player, const char *thing_name, const char *dest_name)
 
             break;
         case TYPE_PROGRAM:
-            notify(player, "You can't link programs to things!");
+            notify(player, "Program links cannot be modified.");
             break;
         default:
             notify(player, "Internal error: weird object type.");
diff --git a/src/mfuns2.c b/src/mfuns2.c
index b5c952b1..dd13aa61 100644
--- a/src/mfuns2.c
+++ b/src/mfuns2.c
@@ -217,6 +217,9 @@ mfn_links(MFUNARGS)
             }
 
         case TYPE_PROGRAM:
+            obj = OWNER(obj);
+            break;
+
         default:
             return "#-1";
     }
diff --git a/src/p_db.c b/src/p_db.c
index a7864210..1cbbb4c6 100644
--- a/src/p_db.c
+++ b/src/p_db.c
@@ -1525,15 +1525,6 @@ prim_getlink(PRIM_PROTOTYPE)
 
     CHECKREMOTE(oper1->data.objref);
 
-    /**
-     * @TODO A years-old suggestion would have this returning the
-     *       link of a program as the home of its owner.  Does this
-     *       make sense?
-     */
-    if (Typeof(oper1->data.objref) == TYPE_PROGRAM) {
-        abort_interp("Illegal object referenced.");
-    }
-
     /**
      * @TODO Combine with other link resolution logic as possible.
      */
@@ -1555,6 +1546,10 @@ prim_getlink(PRIM_PROTOTYPE)
             ref = DBFETCH(oper1->data.objref)->sp.room.dropto;
             break;
 
+        case TYPE_PROGRAM:
+            ref = OWNER(oper1->data.objref);
+            break;
+
         default:
             ref = NOTHING;
             break;
@@ -1595,10 +1590,6 @@ prim_getlinks(PRIM_PROTOTYPE)
 
     CHECKREMOTE(oper1->data.objref);
 
-    if (Typeof(oper1->data.objref) == TYPE_PROGRAM) {
-        abort_interp("Illegal object referenced.");
-    }
-
     my_obj = oper1->data.objref;
 
     switch (Typeof(my_obj)) {
@@ -1645,6 +1636,16 @@ prim_getlinks(PRIM_PROTOTYPE)
 
             break;
 
+        case TYPE_PROGRAM:
+            CHECKOFLOW(2);
+
+            ref = OWNER(my_obj);
+
+            count = 1;
+
+            PushObject(ref);
+            break;
+
         default:
             count = 0;
             break;
@@ -1750,7 +1751,7 @@ prim_setlink(PRIM_PROTOTYPE)
     oper1 = POP();              /* dbref: destination */
     oper2 = POP();              /* dbref: source */
 
-    if (!valid_object(oper1)) {
+    if (!valid_object(oper1) && oper1->data.objref != NOTHING) {
         abort_interp("Invalid object. (2)");
     }
 
@@ -1758,11 +1759,16 @@ prim_setlink(PRIM_PROTOTYPE)
         abort_interp("Invalid object. (1)");
     }
 
-    ref = oper2->data.objref;
-
     /**
      * @TODO Combine with other link setting logic as possible.
      */
+
+    ref = oper2->data.objref;
+
+    if (Typeof(ref) == TYPE_PROGRAM) {
+        abort_interp("Program links cannot be modified. (1)");
+    }
+
     if (oper1->data.objref == NOTHING) {
         if ((mlev < 4) && !permissions(ProgUID, ref)) {
             abort_interp("Permission denied.");
@@ -1784,16 +1790,12 @@ prim_setlink(PRIM_PROTOTYPE)
             DBSTORE(ref, sp.room.dropto, NOTHING);
         }
     } else {
-        if (Typeof(ref) == TYPE_PROGRAM) {
-            abort_interp("Program objects are not linkable. (1)");
-        }
-
         if (!prog_can_link_to(mlev, ProgUID, Typeof(ref), oper1->data.objref)) {
             abort_interp("Can't link source to destination.");
         }
 
         if ((mlev < 4) && !permissions(ProgUID, ref)) {
-                    abort_interp("Permission denied.");
+            abort_interp("Permission denied.");
         }
 
         switch (Typeof(ref)) {
@@ -3488,6 +3490,10 @@ array_getlinks(dbref obj, int pinned)
             array_set_intkey_refval(&nw, count++, PLAYER_HOME(obj));
             break;
 
+        case TYPE_PROGRAM:
+            array_set_intkey_refval(&nw, count++, OWNER(obj));
+            break;
+
         case TYPE_EXIT:
             for (count = 0; count < (DBFETCH(obj)->sp.exit.ndest); count++) {
                 array_set_intkey_refval(&nw, count, (DBFETCH(obj)->sp.exit.dest)[count]);
@@ -3962,6 +3968,10 @@ prim_setlinks_array(PRIM_PROTOTYPE)
                 case TYPE_ROOM:
                     break;
 
+                case TYPE_PROGRAM:
+                    CLEAR(&idx);
+                    abort_interp("Program links cannot be modified. (2)");
+
                 default:
                     CLEAR(&idx);
                     abort_interp("Invalid object. (1)");
diff --git a/src/predicates.c b/src/predicates.c
index 200613b9..32f56623 100644
--- a/src/predicates.c
+++ b/src/predicates.c
@@ -65,7 +65,7 @@ can_link_to(dbref who, object_flag_type what_type, dbref where)
         && (Typeof(where) == TYPE_EXIT || Typeof(where) == TYPE_PROGRAM))
         return 0;
 
-    /* Programs cannot be linked */
+    /* Program links cannot be modified */
     if (what_type == TYPE_PROGRAM)
         return 0;
 
diff --git a/src/set.c b/src/set.c
index 3fd51a82..6ff57dda 100644
--- a/src/set.c
+++ b/src/set.c
@@ -198,6 +198,9 @@ _do_unlink(int descr, dbref player, const char *name, bool quiet)
                 if (!quiet)
                     notify(player, "Player's home reset to default player start room.");
 
+                break;
+            case TYPE_PROGRAM:
+                notify(player, "Program links cannot be modified.");
                 break;
             default:
                 notify(player, "You can't unlink that!");
@@ -361,7 +364,7 @@ do_relink(int descr, dbref player, const char *thing_name,
 
             break;
         case TYPE_PROGRAM:
-            notify(player, "You can't link programs to things!");
+            notify(player, "Program links cannot be modified.");
             return;
         default:
             notify(player, "Internal error: weird object type.");