As part of my daily work at our Documentum customers, I often have to work on files like creating some dummy documents, retrieving the content of a specific document on the file system, etc… Documentum does that well internally and some of it can be done through some UIs, like Documentum Administrator, but you don’t always have access to these UIs and not everything can be done there. Therefore, being able to do that through command-line, iAPI particularly, is a nice-to-have for any people managing Documentum installations. In this blog, I will go through the commands and steps needed to execute particularly useful things.

Import a content from the File System

Let’s start this blog with something quite simple: attaching a content to an existing object in Documentum. For that purpose, I will first fetch said object and then use “setfile” to attach a content to it:

[dmadmin@cs-0 ~]$ r_object_id="0901234580415f00" #dm_document.r_object_id
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ iapi $DOCBASE_NAME -Udmadmin -Pxxx << EOC
> fetch,c,${r_object_id}
> get,c,l,r_content_size
> setfile,c,l,/tmp/file_content_1.docx,msw12
> get,c,l,r_content_size
> save,c,l
> EOC

        OpenText Documentum iapi - Interactive API interface
        Copyright (c) 2020. OpenText Corporation
        All rights reserved.
        Client Library Release 20.2.0000.0082

Connecting to Server using docbase REPO01
[DM_SESSION_I_SESSION_START]info:  "Session 01012345800fc6ff started for user dmadmin."

Connected to OpenText Documentum Server running Release 20.2.00013.0135  Linux64.Oracle
Session id is s0
API> ...
OK
API> ...
113479
API> ...
OK
API> ...
12586
API> ...
OK
API> Bye
[dmadmin@cs-0 ~]$

The content of this document was 113479 bytes before and 12586 bytes after the update.

Export a content to the File System

If you can set a content of an existing object from the file system, you can also do the other way around and export the content to the file system. For that, it’s the “getfile” this time:

[dmadmin@cs-0 ~]$ ls -l /tmp/file_exported*
ls: cannot access /tmp/file_exported*: No such file or directory
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ iapi $DOCBASE_NAME -Udmadmin -Pxxx << EOC
> fetch,c,${r_object_id}
> getfile,c,l,/tmp/file_exported.docx
> EOC

        OpenText Documentum iapi - Interactive API interface
        Copyright (c) 2020. OpenText Corporation
        All rights reserved.
        Client Library Release 20.2.0000.0082

Connecting to Server using docbase REPO01
[DM_SESSION_I_SESSION_START]info:  "Session 01012345800fc712 started for user dmadmin."

Connected to OpenText Documentum Server running Release 20.2.00013.0135  Linux64.Oracle
Session id is s0
API> ...
OK
API> ...
/tmp/file_exported.docx
API> Bye
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ ls -l /tmp/file_exported*
-rw-r----- 1 dmadmin dmadmin  12586 Dec 19 12:10 /tmp/file_exported.docx
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ diff /tmp/file_exported.docx /tmp/file_content_1.docx
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ md5sum /tmp/file_exported.docx /tmp/file_content_1.docx
a84d45bd4e57a7c105bae574b32985ae  /tmp/file_exported.docx
a84d45bd4e57a7c105bae574b32985ae  /tmp/file_content_1.docx
[dmadmin@cs-0 ~]$

As you can see, the exported file is the same as the file we uploaded earlier, as we can expect. In this case, you need to know the extension of the file to set a correct name. You can, of course, export it without extension or with a dummy one, as long as you know which application will be able to open it (if you need to open it for any investigation or similar reasons).

Import a content from the File System on a protected/immutable/frozen object (e.g.: TBO)

In case you have to work on some protected/immutable/frozen objects, such as the TBO (immutable), then you will have to first “checkout” and then “checkin” the object to be able to update its content, still with the “setfile” in the middle:

[dmadmin@cs-0 ~]$ iapi $DOCBASE_NAME -Udmadmin -Pxxx << EOC
> fetch,c,${r_object_id}
> checkout,c,l
> setfile,c,l,/tmp/lstbo.jar
> checkin,c,l
> EOC

        OpenText Documentum iapi - Interactive API interface
        Copyright (c) 2020. OpenText Corporation
        All rights reserved.
        Client Library Release 20.2.0000.0082

Connecting to Server using docbase REPO01
[DM_SESSION_I_SESSION_START]info:  "Session 01012345800fc745 started for user dmadmin."

Connected to OpenText Documentum Server running Release 20.2.00013.0135  Linux64.Oracle
Session id is s0
API> ...
OK
API> ...
0901234580415f00
API> ...
OK
API> ...
090123458042dff9
API> Bye
[dmadmin@cs-0 ~]$

In this case, you don’t need to save the object, the “checkin” will do that automatically when it creates a new version with the new content.

Create a new file

If you don’t have an object yet, you can create a new one with all the needed details. For that, you will need to use “create“, then all the needed “set“/”setfile“/”link“/whatever as required for your new document:

[dmadmin@cs-0 ~]$ ls -ltr /tmp/file_content*
-rwxr-x--- 1 dmadmin dmadmin  12586 Dec 15 16:45 /tmp/file_content_1.docx
-rwxr-x--- 1 dmadmin dmadmin 113479 Dec 15 16:46 /tmp/file_content_2.docx
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ iapi $DOCBASE_NAME -Udmadmin -Pxxx << EOC
> create,c,dm_document
> set,c,l,object_name
> test_document
> setfile,c,l,/tmp/file_content_2.docx,msw12
> link,c,l,'/dmadmin'
> get,c,l,r_content_size
> save,c,l
> EOC

        OpenText Documentum iapi - Interactive API interface
        Copyright (c) 2020. OpenText Corporation
        All rights reserved.
        Client Library Release 20.2.0000.0082

Connecting to Server using docbase REPO01
[DM_SESSION_I_SESSION_START]info:  "Session 01012345800fc770 started for user dmadmin."

Connected to OpenText Documentum Server running Release 20.2.00013.0135  Linux64.Oracle
Session id is s0
API> ...
090123458042e00d
API> SET> ...
OK
API> ...
OK
API> ...
OK
API> ...
113479
API> ...
OK
API> Bye
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ r_object_id="090123458042e00d"
[dmadmin@cs-0 ~]$

Once the document is created, you can then work on it as usual, changing its content, creating new versions, deleting it, etc…

Retrieve the File System path of a file from the Repo

From time to time, you will need to investigate the source content of an object, maybe because it’s not present anymore or it’s not what you expected, things like that. For that purpose, you have two main solutions, the easy and the hard way. The easy one will just use the “getpath” command provided and the hard one will use some “get” on different objects to be able to reconstruct the path yourself (why would you want to do it the hard way? to show you can do it, of course! :D).

The easy way:

[dmadmin@cs-0 ~]$ # Easy way
[dmadmin@cs-0 ~]$ iapi $DOCBASE_NAME -Udmadmin -Pxxx << EOC
> fetch,c,${r_object_id}
> getpath,c,l
> EOC

        OpenText Documentum iapi - Interactive API interface
        Copyright (c) 2020. OpenText Corporation
        All rights reserved.
        Client Library Release 20.2.0000.0082

Connecting to Server using docbase REPO01
[DM_SESSION_I_SESSION_START]info:  "Session 01012345800fc7a4 started for user dmadmin."

Connected to OpenText Documentum Server running Release 20.2.00013.0135  Linux64.Oracle
Session id is s0
API> ...
OK
API> ...
/data/dctm/REPO01/content_storage_01/00012345/80/0a/50/f7.docx
API> Bye
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ diff /tmp/file_content_2.docx /data/dctm/REPO01/content_storage_01/00012345/80/0a/50/f7.docx
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ md5sum /tmp/file_content_2.docx /data/dctm/REPO01/content_storage_01/00012345/80/0a/50/f7.docx
fc3afe90ae970ccab544b3005be1f6ea  /tmp/file_content_2.docx
fc3afe90ae970ccab544b3005be1f6ea  /data/dctm/REPO01/content_storage_01/00012345/80/0a/50/f7.docx
[dmadmin@cs-0 ~]$

A “long” version of the hard way, that requires multiple sessions (because you need to get some IDs to use them on follow-up queries):

[dmadmin@cs-0 ~]$ # Hard way - long version
[dmadmin@cs-0 ~]$ iapi $DOCBASE_NAME -Udmadmin -Pxxx << EOC
> fetch,c,${r_object_id}
> get,c,l,i_contents_id
> EOC

        OpenText Documentum iapi - Interactive API interface
        Copyright (c) 2020. OpenText Corporation
        All rights reserved.
        Client Library Release 20.2.0000.0082

Connecting to Server using docbase REPO01
[DM_SESSION_I_SESSION_START]info:  "Session 01012345800fc7ab started for user dmadmin."

Connected to OpenText Documentum Server running Release 20.2.00013.0135  Linux64.Oracle
Session id is s0
API> ...
OK
API> ...
06012345800ae6d4
API> Bye
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ i_contents_id="06012345800ae6d4"
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ iapi $DOCBASE_NAME -Udmadmin -Pxxx << EOC
> fetch,c,${i_contents_id}
> get,c,l,storage_id
> get,c,l,data_ticket
> EOC

        OpenText Documentum iapi - Interactive API interface
        Copyright (c) 2020. OpenText Corporation
        All rights reserved.
        Client Library Release 20.2.0000.0082

Connecting to Server using docbase REPO01
[DM_SESSION_I_SESSION_START]info:  "Session 01012345800fc7ae started for user dmadmin."

Connected to OpenText Documentum Server running Release 20.2.00013.0135  Linux64.Oracle
Session id is s0
API> ...
OK
API> ...
2801234580000100
API> ...
-2146807561
API> Bye
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ storage_id="2801234580000100"
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ iapi $DOCBASE_NAME -Udmadmin -Pxxx << EOC
> fetch,c,${storage_id}
> get,c,l,root
> EOC

        OpenText Documentum iapi - Interactive API interface
        Copyright (c) 2020. OpenText Corporation
        All rights reserved.
        Client Library Release 20.2.0000.0082

Connecting to Server using docbase REPO01
[DM_SESSION_I_SESSION_START]info:  "Session 01012345800fc842 started for user dmadmin."

Connected to OpenText Documentum Server running Release 20.2.00013.0135  Linux64.Oracle
Session id is s0
API> ...
OK
API> ...
storage_01
API> Bye
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ root="storage_01"
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ iapi $DOCBASE_NAME -Udmadmin -Pxxx << EOC
> retrieve,c,dm_location where object_name='${root}'
> get,c,l,file_system_path
> EOC

        OpenText Documentum iapi - Interactive API interface
        Copyright (c) 2020. OpenText Corporation
        All rights reserved.
        Client Library Release 20.2.0000.0082

Connecting to Server using docbase REPO01
[DM_SESSION_I_SESSION_START]info:  "Session 01012345800fc848 started for user dmadmin."

Connected to OpenText Documentum Server running Release 20.2.00013.0135  Linux64.Oracle
Session id is s0
API> ...
3a0123458000013f
API> ...
/data/dctm/REPO01/content_storage_01
API> Bye
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ file_system_path="/data/dctm/REPO01/content_storage_01"
[dmadmin@cs-0 ~]$

As you can see above, there are quite a few steps to get the needed information manually. From that point, you need to transform the “data_ticket” from a decimal to a hexadecimal signed 2’s complement on 8 digits. You can do that with an online website like this one, put your “data_ticket” into the “Enter decimal number”, click on “Convert” and look at the “Hex signed 2’s complement (8 digits)” value. You can also do that through bash (basically, you add 2^32 to the value of “data_ticket” and then you transform that value to hexadecimal):

[dmadmin@cs-0 ~]$ # Add 2^32 to data_ticket and then transform to hexadecimal
[dmadmin@cs-0 ~]$ data_ticket="-2146807561"
[dmadmin@cs-0 ~]$ hex_path="$(echo ${data_ticket} | gawk '{printf("%x\n", $0 + 2**32)}')"
[dmadmin@cs-0 ~]$ echo ${hex_path}
800a50f7
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ # Add / every 2 characters for the folder separator
[dmadmin@cs-0 ~]$ doc_path="$(echo ${hex_path} | sed 's,..,/&,g;s,^/,,')"
[dmadmin@cs-0 ~]$ echo ${doc_path}
80/0a/50/f7
[dmadmin@cs-0 ~]$

With the above, we have:

  1. file_system_path = /data/dctm/REPO01/content_storage_01
  2. data_ticket = -2146807561
    1. hex value from data_ticket = 800a50f7
      1. document path on file system based on hex value from data_ticket = 80/0a/50/f7

Therefore, the file system path of this document is: /data/dctm/REPO01/content_storage_01//80/0a/50/f7* (concatenation of “file_system_path” ++ docbase id ++ doc path derived from the “data_ticket” (hex value split every 2 characters by a slash)). The extension of the file will be the present on the file system as well, you could fetch it from the repository too, but there is no real need, since there should be only 1 result anyway. To verify that you can find the file on the file system:

[dmadmin@cs-0 ~]$ echo ${r_object_id}
090123458042e00d
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ # Extract the Docbase ID from any r_object_id
[dmadmin@cs-0 ~]$ docbase_id="00${r_object_id:2:6}"
[dmadmin@cs-0 ~]$ echo ${docbase_id}
00012345
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ # Concatenate the different variables needed to form the path
[dmadmin@cs-0 ~]$ echo ${file_system_path}/${docbase_id}/${doc_path}
/data/dctm/REPO01/content_storage_01/00012345/80/0a/50/f7
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ # Verify that the file exists
[dmadmin@cs-0 ~]$ ls -ltr ${file_system_path}/${docbase_id}/${doc_path}*
-rw------- 1 dmadmin dmadmin 113479 Dec 19 10:06 /data/dctm/REPO01/content_storage_01/00012345/80/0a/50/f7.docx
[dmadmin@cs-0 ~]$

We find the same file as what the “getpath” command returned on the “Easy way” section above, so that’s a success… As you can see it’s a little bit more gymnastic than simply using a command that OpenText provides (for a good reason!).

If you want a shorter version of the “Hard way”, to avoid the multiple jumps between objects, you can use something like the following, where only the “r_object_id” is required:

[dmadmin@cs-0 ~]$ # Hard way - short version
[dmadmin@cs-0 ~]$ iapi $DOCBASE_NAME -Udmadmin -Pxxx << EOC
> retrieve,c,dmr_content where r_object_id IN (select i_contents_id from dm_document where r_object_id='${r_object_id}')
> get,c,l,data_ticket
> retrieve,c,dm_location where object_name IN (select root from dm_filestore where r_object_id IN (select storage_id from dmr_content where r_object_id IN (select i_contents_id from dm_document where r_object_id='${r_object_id}')))
> get,c,l,file_system_path
> EOC

        OpenText Documentum iapi - Interactive API interface
        Copyright (c) 2020. OpenText Corporation
        All rights reserved.
        Client Library Release 20.2.0000.0082

Connecting to Server using docbase REPO01
[DM_SESSION_I_SESSION_START]info:  "Session 01012345800fc8ec started for user dmadmin."

Connected to OpenText Documentum Server running Release 20.2.00013.0135  Linux64.Oracle
Session id is s0
API> ...
06012345800ae6d4
API> ...
-2146807561
API> ...
3a0123458000013f
API> ...
/data/dctm/REPO01/content_storage_01
API> Bye
[dmadmin@cs-0 ~]$

With that, the same “data_ticket” and “file_system_path” are returned, and you need to do the transformation as it was done previously, to get the final path on the file system…

Retrieve the Repo Object from the File System path of a file

If you can do it one way, you can of course do it the other way as well. This could be useful if you know some files were removed on the file system or some were corrupted because of the storage, and you want to find which documents they relates to in Documentum. As previously, you can use the online website to transform back from a hexadecimal 2’s complement to a decimal (10 digits) or you can do it via bash:

[dmadmin@cs-0 ~]$ ls -l /data/dctm/REPO01/content_storage_01/00012345/80/0a/50/f7.docx
-rw------- 1 dmadmin dmadmin 113479 Dec 19 10:06 /data/dctm/REPO01/content_storage_01/00012345/80/0a/50/f7.docx
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ # Setting the initial variable with the path of existing file
[dmadmin@cs-0 ~]$ file_path="/data/dctm/REPO01/content_storage_01/00012345/80/0a/50/f7.docx"
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ # Extracting only the last 3 folders + file name
[dmadmin@cs-0 ~]$ doc_path="$(echo ${file_path} | awk -F / -v OFS=/ '{ print $(NF-3), $(NF-2), $(NF-1), $NF }')"
[dmadmin@cs-0 ~]$ echo ${doc_path}
80/0a/50/f7.docx
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ # Removing the file extension and / separator
[dmadmin@cs-0 ~]$ hex_path="$(echo ${doc_path} | sed 's,/,,g;s,\..*,,')"
[dmadmin@cs-0 ~]$ echo ${hex_path}
800a50f7
[dmadmin@cs-0 ~]$
[dmadmin@cs-0 ~]$ # Transform to decimal and then substract 2^32
[dmadmin@cs-0 ~]$ data_ticket="$(( 16#${hex_path} - 2**32 ))"
[dmadmin@cs-0 ~]$ echo ${data_ticket}
-2146807561
[dmadmin@cs-0 ~]$

From there, Documentum can give you the document that is using this “data_ticket“:

[dmadmin@cs-0 ~]$ iapi $DOCBASE_NAME -Udmadmin -Pxxx << EOC
> ?,c,select r_object_id, object_name from dm_sysobject(all) where r_object_id in (select parent_id_i from dmr_content where data_ticket='${data_ticket}');
> EOC

        OpenText Documentum iapi - Interactive API interface
        Copyright (c) 2020. OpenText Corporation
        All rights reserved.
        Client Library Release 20.2.0000.0082

Connecting to Server using docbase REPO01
[DM_SESSION_I_SESSION_START]info:  "Session 01012345800fc9b3 started for user dmadmin."

Connected to OpenText Documentum Server running Release 20.2.00013.0135  Linux64.Oracle
Session id is s0
API> 
r_object_id       object_name
----------------  --------------
090123458042e00d  test_document
(1 row affected)

API> Bye
[dmadmin@cs-0 ~]$

That is indeed the document that was created previously for this blog, so that’s another success.

There are plenty of other useful iAPI commands to use but I think that’s long enough for this blog. Maybe another one later?