25 Q& a Two What Exactly Are the Privileges of Special Processes

25 Q&A Two- What Exactly Are the Privileges of Special Processes- #

Hello, I’m Wen Ming.

In the column update until now, we have completed the second section of the OpenResty API series. Congratulations on keeping up with the material and actively learning and practicing, while also sharing your thoughts enthusiastically.

Many valuable questions have been raised in the comments, and I have already replied to most of them in the app. Today, I have specifically selected some questions that are either difficult to respond to on mobile or are more typical and interesting. I will address these questions in today’s FAQ to ensure that no one misses any important points.

Now let’s take a look at these six questions for today.

Question 1: Privileged Process Permissions #

Q: I would like to ask about privileged processes. If I start OpenResty as a regular user, how can I obtain root privileges? Also, could you please introduce some scenarios where privileged processes are used?

A: Actually, the permissions of a privileged process are the same as those of the master process. If you start OpenResty as a regular user, then the master process has regular user permissions, and therefore the privileged process does not have any special privileges.

This should be easy to understand. Processes started by regular users will never have root permissions, regardless of the situation.

As for scenarios where privileged processes are used, we generally use them to perform tasks that require high permissions, such as log cleanup and restarting OpenResty itself. However, you should not assign tasks of worker processes to privileged processes. This is not because privileged processes are incapable of handling them, but rather due to security risks.

I have encountered a developer who assigned timer tasks to privileged processes. Why did he do this? Because there is only one privileged process, ensuring timers won’t be started repeatedly.

You might think this is clever, as it avoids using something like worker.id. However, don’t forget that if the tasks of these timers are related to user input, this essentially leaves a backdoor open. Clearly, this is very dangerous.

Question 2: Phases and Debugging #

Q: Teacher, no matter which phase I run ngx.say('hello'), will OpenResty respond directly to the client after executing the remaining code in this phase, without continuing in other phases? That’s what I found in my tests.

A: Actually, it’s not the case. Let’s take a look at the sequence diagram of its execution phases here:

You can do a test by using ngx.say in the content phase first, and then try using ngx.log to print out the logs in the log or body filter phase.

In this column, I did not specifically mention the issue of debugging code in OpenResty, which is also a common confusion for developers. I will take the opportunity to discuss it here.

In fact, code debugging in OpenResty does not have advanced features like breakpoints (there are some paid plugins available, but I haven’t used them). The only way to debug is to use ngx.say and ngx.log to view the output. As far as I know, developers including the authors and contributors of OpenResty usually debug in this way. Therefore, you need to have strong test cases and debug logs as evidence.

Question 3: ngx.exit and Hands-on Experiment #

Q: Teacher, regarding this sentence in the article: “In the HTTP status codes of OpenResty, there is a special constant: ngx.OK. When ngx.exit(ngx.OK) is called, the request will exit the current processing phase and move on to the next phase instead of directly returning to the client.”

I remember ngx.OK cannot be considered an HTTP status code, its corresponding value is 0. My understanding is:

  • When ngx.exit(ngx.OK), ngx.exit(ngx.ERROR) and ngx.exit(ngx.DECLINED) are called, the request will exit the current processing phase and move on to the next phase.
  • When ngx.exit(ngx.HTTP_*) is called with various HTTP status codes as parameters, it will directly respond to the client.

I’m not sure if my understanding is correct.

A: Regarding your first question, it is true that ngx.OK is not an HTTP status code, but rather a constant in OpenResty with a value of 0.

As for your second question, the official documentation of ngx.exit actually provides a clear answer:

When status >= 200 (i.e., ngx.HTTP_OK and above), it will interrupt the execution of the current request and return the status code to nginx.

When status == 0 (i.e., ngx.OK), it will only quit the current phase handler (or the content handler if the content_by_lua* directive is used) and continue to run later phases (if any) for the current request.

However, the documentation does not mention how OpenResty handles ngx.exit(ngx.ERROR) and ngx.exit(ngx.DECLINED). We can conduct a test to find out. For example, by adding the following code:

location /lua {
        rewrite_by_lua "ngx.exit(ngx.ERROR)";
        echo hello;
    }

Clearly, when accessing this location, you will see that the HTTP response code is empty and the response body is empty without entering the next execution phase.

In fact, as I mentioned before, during the learning process of OpenResty, as you delve deeper, you will likely encounter situations where the documentation and test cases cannot answer your questions. In such cases, you need to build your own test cases to verify your thoughts. You can test manually or add them to the test suite built in test::nginx.

Question 4: Variables and Competition #

Q: Hello teacher, I have several questions I would like to ask.

  1. It was mentioned earlier that the scope of the ngx.var variable is between the nginx C and lua-nginx-module modules. I don’t quite understand this. From the perspective of a request, does it refer to a single request in a working process?
  2. My understanding is that when we manipulate variables within a module, if there is a blocking operation between two operations, there may be competition. So, if there is no blocking operation between two operations and the current process enters the ready queue when the CPU time expires, can this result in competition?

A: Let’s examine these questions one by one.

Firstly, regarding the question about the ngx.var variable, your understanding is correct. In fact, the lifespan of the ngx.var variable is the same as that of the request, and it will disappear when the request ends. However, its advantage is that data can be passed between C modules and Lua code, which is not possible with other methods.

Secondly, regarding the question about variable competition, in fact, competition can occur as long as there is a yield operation between two operations, rather than just blocking operations; competition does not occur when there are blocking operations. In other words, as long as you do not give the initiative to Nginx’s event loop, there will be no competition.

Question 5: Do we need to add locks for shared dictionary operations? #

Q: Teacher, do we need to add locks if multiple workers are concurrently storing data? For example, in the code snippet below:

resty --shdict 'dogs 10m' -e 'local dogs = ngx.shared.dogs
local lock= ngx.xxxx.lock
lock.lock()
 dogs:set("Jim", 8)
lock.unlock()
 local v = dogs:get("Jim")
 ngx.say(v)
'

A: Actually, you don’t need to add locks yourself. The operations on the shared dictionary (shared dict) are atomic, whether it is get or set. OpenResty has already taken care of this kind of locking-like operation for you.

Question 6: How to update the time in OpenResty? #

Q: Does ngx.now() get the time during the resume function’s stack restoration phase?

A: Nginx is designed with performance as the main focus, so it caches the time. This can be confirmed from the source code of ngx.now():

static int
ngx_http_lua_ngx_now(lua_State *L)
{
    ngx_time_t              *tp;

    tp = ngx_timeofday();

    lua_pushnumber(L, (lua_Number) (tp->sec + tp->msec / 1000.0L));

    return 1;
}

We can see that the underlying function behind ngx.now() is actually Nginx’s ngx_timeofday() function. And the ngx_timeofday() function is actually a macro definition:

#define ngx_timeofday()      (ngx_time_t *) ngx_cached_time

The value of ngx_cached_time is only updated in the ngx_time_update function.

So the question is simplified to: when is ngx_time_update called? If you trace it in the Nginx source code, you will find that ngx_time_update is called in the event loop, and that answers the question.

Through this question, you should also realize the benefit of open-source projects. You can search for answers in the source code based on the hints, which gives you a certain sense of accomplishment.

Those are the main questions I have answered today. Finally, feel free to write down your further questions in the comments section. I will continue to provide answers. I hope that through communication and Q&A, I can help you convert your learning into achievement. You are also welcome to share this article and let’s communicate and progress together.